Skip to main content

This is documentation for Caché & Ensemble. See the InterSystems IRIS version of this content.Opens in a new tab

For information on migrating to InterSystems IRISOpens in a new tab, see Why Migrate to InterSystems IRIS?

UPDATE

指定されたテーブルの指定された列に新しい値を設定します。

Synopsis

UPDATE [%NOFPLAN] [restriction] table-ref [[AS] t-alias]
   value-assignment-statement 
   [FROM [optimize-option] table-ref1 [[AS] t-alias]
         {,table-ref2 [[AS] t-alias]} ]
   [WHERE condition-expression]

UPDATE [restriction] table-ref [[AS] t-alias]
   value-assignment-statement 
   [WHERE CURRENT OF cursor]

value-assignment-statement ::=
   SET column1 = scalar-expression1 {,column2 = scalar-expression2} ...  |
   [ (column1{,column2} ...) ] VALUES (scalar-expression1 {,scalar-expression2} ...)  |
   VALUES :array()

引数

%NOFPLAN オプション — %NOFPLAN キーワードは、Caché がこの操作の凍結されたプラン (ある場合) を無視して、新しいクエリ・プランを生成することを指定します。凍結されたプランは保持されますが、使用されません。 詳細は、"Caché SQL 最適化ガイド" の "凍結プラン" を参照してください。
restriction オプション — %NOLOCK、%NOCHECK、%NOINDEX、%NOTRIGGER のキーワードのうちの 1 つ、またはこれらのキーワードの空白で区切られたリスト。
table-ref データを更新する既存のテーブルの名前。テーブルで更新を実行するビューを指定できます。この引数でテーブル値関数や JOIN 構文を指定することはできません。
AS t-alias オプションtable-ref (テーブルまたはビュー) の名前のエイリアス。エイリアスは有効な識別子である必要があります。AS キーワードはオプションです。
FROM table-ref

オプション — 更新する行を特定する際に使用されるテーブルを指定するために使用される FROM 節。

複数のテーブルは、コンマ区切りのリストとして指定するか、ANSI 結合キーワードで関連付けることができます。テーブルあるいはビューのあらゆる組み合わせを指定できます。ここで 2 つの table-ref の間にコンマを指定する場合、Caché は複数のテーブルに CROSS JOIN を実行して、JOIN 処理の結果テーブルからデータを取得します。ここで 2 つの table-ref の間に ANSI 結合キーワードを指定する場合、Caché は指定された結合処理を実行します。詳細は、このドキュメントの "JOIN" のページを参照してください。

オプションで、クエリ実行を最適化するために、1 つ以上の optimize-option キーワードを指定できます。使用可能なオプションは、%ALLINDEX、%FIRSTTABLE tablename、%FULL、%INORDER、%IGNOREINDICES、%NOFLATTEN、%NOMERGE、%NOSVSO、%NOTOPOPT、%NOUNIONOROPT、および %STARTTABLE です。詳細は、"FROM" 節を参照してください。

WHERE condition-expression オプション — 更新する行を決定する 1 つまたは複数のブーリアン述語を指定します。WHERE 節 (または WHERE CURRENT OF 節) が指定されていない場合、UPDATE はテーブル内のすべての行を更新します。詳細は、"WHERE" を参照してください。
WHERE CURRENT OF cursor オプション : 埋め込み SQL のみUPDATE 操作が、現在のカーソル位置のレコードを更新することを指定します。WHERE CURRENT OF 節または WHERE 節を指定することができます (両方は不可)。詳細は、"WHERE CURRENT OF" を参照してください。
column オプション — 既存の列名。複数の列名はコンマ区切りのリストで指定します。省略した場合は、すべての列が更新されます。
scalar-expression スカラ式で表す列データの値。複数のデータ値はコンマ区切りリストで指定します。各データ値は列の並びに対応します。
array() 埋め込み SQL のみ — ホスト変数として指定する値の配列。配列の最下位の添え字は指定しないでください。:myupdates():myupdates(5,)、および :myupdates(1,1,) はすべて、有効な指定になります。

概要

UPDATE コマンドは、テーブルの列の既存値を変更します。テーブルのデータの更新には、直接行う方法、ビューを使用した方法、または括弧で囲んだサブクエリを使用した方法があります。ビューを使用して更新する場合は、CREATE VIEW で説明されているように、必要条件や制限事項に従います。

UPDATE コマンドにより、1 つ以上の新しい列の値が、それらの列を含む既存のベース・テーブルの 1 つ以上の行に入ります。データ値の列への割り当ては、value-assignment-statement を使用して行われます。既定では、value-assignment-statement はテーブルのすべての行で更新されます。

UPDATE を使用する場合、一般的には condition-expression に基づいて更新する行を指定します。既定では、UPDATE 処理はテーブルのすべての行をチェックし、condition-expression を満たすすべての行を更新します。condition-expression を満たす行がない場合、UPDATE は正常に終了して SQLCODE=100 (データがこれ以上ありません) を設定します。

WHERE 節または WHERE CURRENT OF 節を指定することができます (両方は不可)。WHERE CURRENT OF 節が使用される場合、UPDATE は現在のカーソル位置のレコードを更新します。指定位置での実行の詳細は、"WHERE CURRENT OF" を参照してください。

また、UPDATE 操作により、%ROWCOUNT ローカル変数に更新された行数が設定され、%ROWID ローカル変数に最後に更新された行の行 ID が設定されます。

既定では、UPDATE オペレーションは全か無かのイベントです。指定された行および列をすべて更新するか、まったくしないかのいずれかです。

INSERT OR UPDATE

INSERT OR UPDATE 文は INSERT 文のバリアントであり、挿入操作と更新操作の両方を実行します。この文は最初に、挿入操作を実行しようとします。UNIQUE KEY 違反が原因で挿入要求に失敗した場合 (いずれかの一意キーのフィールドについて、この挿入対象として指定された行と同じ値を持つ既存の行が存在する場合)、挿入要求はその行に対する更新要求に自動的に変化し、INSERT OR UPDATE が指定されたフィールド値を使用して既存の行を更新します。

特権

更新を実行するには、指定したテーブル (またはビュー) のテーブルレベルの UPDATE 特権、または指定した列の列レベルの UPDATE 特権のいずれかが必要です。行内のすべてのフィールドを更新する場合、列レベルの特権は GRANT コマンドで名付けられたすべてのテーブル列を管理し、テーブルレベルの特権は、特権が割り当てられた後に追加されたものも含み、すべてのテーブル列を管理します。必要な特権を持っていないと SQLCODE -99 エラー (特権違反) になります。%CHECKPRIV コマンドを呼び出すことにより、現在のユーザが UPDATE 特権を持っているかどうかを確認できます。$SYSTEM.SQL.CheckPriv()Opens in a new tab メソッドを呼び出すことにより、指定のユーザがテーブルレベルの UPDATE 特権を持っているかどうかを確認できます。特権の割り当てについては、"GRANT" コマンドを参照してください。

プロパティが ReadOnly として定義されている場合、対応するテーブル・フィールドも ReadOnly として定義されています。ReadOnly フィールドに値を割り当てるには、InitialExpression または SqlComputed を使用する必要があります。列レベルの ReadOnly (SELECT または REFERENCES) 特権を持っているフィールドの値 (NULL 値であっても) を更新しようとすると、SQLCODE -138 エラー : "読み取り専用フィールドをINSERT/UPDATEすることはできません" が発生します。

WHERE 節のフィールドに対しては、これらのフィールドを更新するかどうかにかかわらず、SELECT 特権が必要です。これらのフィールドが UPDATE フィールド・リストに含まれる場合には、これらのフィールドに対する SELECT 特権と UPDATE 特権の両方が必要です。以下の例では、Name フィールドには (少なくとも) 列レベルの SELECT 特権が必要です。

UPDATE Sample.Employee (Salary) VALUES (1000000) WHERE Name='Smith, John'

上記の例では、Salary フィールドには列レベルの UPDATE 特権のみが必要です。

値の割り当て

さまざまな方法で新しい値を、指定した列に割り当てることができます。

  • SET キーワードを使用して、1 つ以上の column = scalar-expression のペアをコンマ区切りリストとして指定します。以下に例を示します。

    SET StatusDate='05/12/06',Status='Purged'
    
  • VALUES キーワードを使用して、列のリストとそれに対応するスカラ式のリストを指定します。以下に例を示します。

    (StatusDate,Status) VALUES ('05/12/06','Purged')
    

    スカラ式の値を列のリストに割り当てる場合、指定する列ごとにスカラ式が必要です。

  • 列リストを指定せずに VALUES キーワードを使用する場合、行の列の列順と暗黙的に対応するスカラ式のリストを指定します。以下の例では、テーブル内のすべての列を指定し、Address 列を更新するためのリテラル値を指定しています。

    VALUES (Name,DOB,'22 Main St. Anytown MA 12345',SSN)
    

    値を暗黙の列リストに割り当てるときには、DDL で列が定義されている順序で、すべての更新可能フィールドに値を指定する必要があります。(更新できない RowId 列は指定されません。)これらの値は、新規の値を指定するリテラルまたは既存の値を指定するフィールド名のいずれかにすることができます。プレースホルダとしてのコンマを指定したり、末尾のフィールドを省略したりすることはできません。

  • 列リストなしで VALUES キーワードを使用すると、数値添え字が列番号と対応している、添え字付き配列が指定されます。これには更新できない RowId が列番号 1 として含まれます。例えば以下のようになります。

    VALUES :myarray()
    

    この値の割り当ては、埋め込み SQL からホスト変数を使用することでのみ実行できます。その他すべての値の割り当てと異なり、この使用方法では (実行時に配列を追加することで) 更新する列の指定を実行時まで遅らせることができます。その他すべてのタイプの更新では、コンパイル時に更新する列を指定する必要があります。詳細は、"Caché SQL の使用法" の “埋め込み SQL の使用法” の章の “添え字付き配列としてのホスト変数” を参照してください。

これらの UPDATE の各タイプを示すプログラム例については、後述の "" のセクションを参照してください。

DISPLAY から LOGICAL へのデータ変換

データは LOGICAL モード形式で格納されます。例えば、日付は日数を示す整数値として格納されます。UPDATE 操作で指定された LOGICAL モード形式でないデータは、LOGICAL モード形式への変換が必要です。コンパイルされた SQL では、DISPLAY 形式または ODBC 形式から LOGICAL 形式への UPDATE データ値の自動変換をサポートします。UPDATE データの自動変換には、コンパイル時には SQL が RUNTIME モードを指定することと、実行時には SQL が LOGICAL モード環境で実行することの 2 つの要素が必要です。

  • 埋め込み SQL では、#SQLCompile Select=runtime を指定すると、Caché により、データ値を表示形式から LOGICAL モード格納形式に変換するコードで SQL 文がコンパイルされます。Caché は、このモード変換を単一の値と値の配列の両方に対して実行します。詳細は、"Caché ObjectScript の使用法" の “ObjectScript マクロとマクロ・プリプロセッサ” の章にある "#SQLCompile Select" を参照してください。

  • SQL の CREATE FUNCTIONCREATE METHOD、または CREATE PROCEDURE 文では、SELECTMODE RUNTIME を指定すると、Caché により、データ値を表示形式から LOGICAL モード格納形式に変換するコードで SQL 文がコンパイルされます。

UPDATE データは、DISPLAY 形式 (6/17/2011 など)、ODBC 形式 (2011-06-17 など)、または LOGICAL 形式 (62259 など) のいずれかの形式となります。SQL 実行環境が LOGICAL モードの場合は、データは LOGICAL 形式で格納されます。これは、すべての Caché SQL 実行環境の既定のモードです。

以下のように、SQL 実行環境で、選択モードを LOGICAL に明示的に設定できます。

SQLCODE エラー

既定では、複数行の UPDATE はアトミック処理です。1 行または複数行の更新できない行があると、UPDATE 処理は失敗し、どの行も更新されません。Caché は、UPDATE の成功または失敗を示す変数 SQLCODE を設定します。操作が失敗した場合は、%msg も設定します。テーブルを更新するには、以下に示すように、更新がすべてのテーブル、列名、および値の各要件を満たしている必要があります。

テーブル :

  • テーブルは、現在の (または指定された) ネームスペースに存在している必要があります。指定されたされたテーブルが見つからない場合、Caché は SQLCODE -30 エラーを発行します。

  • テーブルを READONLY として定義することはできません。読み取り専用テーブルを参照する UPDATE をコンパイルしようとすると、SQLCODE -115 エラーが返されます。このエラーは実行時にのみ発生するのではなく、コンパイル時にも発生するようになったことに注意してください。"Caché オブジェクトの使用法" の "永続クラスのその他のオプション" の章で READONLY オブジェクトの説明を参照してください。

  • 他のプロセスはテーブルを IN EXCLUSIVE MODE でロックできません。ロックされているテーブルを更新しようとすると、SQLCODE -110 エラーになり、%msg は "RowID = '10' の行でテーブル 'Sample.Person' の UPDATE ロックを獲得できません" になります。SQLCODE -110 エラーは、UPDATE 文が更新する最初のレコードを検出した場合にのみ発生し、タイムアウト期間内はロックできません。

  • UPDATE で、存在しないフィールドを指定すると、SQLCODE -29 が発行されます。指定したテーブルのために定義されているすべてのフィールド名をリストするには、"Caché SQL の使用法" の “テーブルの定義” の章の "列の名前と番号" を参照してください。フィールドは存在するが UPDATE コマンドの WHERE 節の条件を満たすフィールド値がない場合は、影響を受ける行はないため、SQLCODE 100 (データの末尾) が発行されます。

  • ビュー経由でテーブルを更新する場合、ビューを WITH READ ONLY として定義することはできません。これを実行しようとすると、SQLCODE -35 エラーが返されます。詳細は、"CREATE VIEW" コマンドを参照してください。

列名と値 :

  • 更新に重複するフィールド名を含めることはできません。同じ名前の 2 つのフィールドを指定する更新を実行しようとすると、SQLCODE -377 エラーが返されます。

  • 別の同時プロセスによってロックされているフィールドは更新できません。これを実行しようとすると、SQLCODE -110 エラーが返されます。大量の更新を実行したことによって <LOCKTABLEFULL> エラーが発生した場合にも、この SQLCODE エラーが発生することがあります。

  • 整数カウンタ・フィールドは更新できません。これらのフィールドは変更不可です。変更すると、以下のエラーが生成されます : RowID フィールド (SQLCODE -107)、IDENTITY フィールド (SQLCODE -107)、SERIAL (%Library.CounterOpens in a new tab) フィールド (SQLCODE -105)、ROWVERSION フィールド (SQLCODE -138)。これらのフィールドの値はシステムで生成され、ユーザは変更できません。ユーザはカウンタ・フィールドの初期値を挿入できても、値を更新することはできません。

    1 つの例外は、既存のデータを含むテーブルに SERIAL (%Library.CounterOpens in a new tab) フィールドを追加する場合です。この追加されたカウンタ・フィールドの既存のレコードには、NULL が含まれます。この場合、UPDATE を使用して NULL を整数値に変更できます。詳細は、"ALTER TABLE" コマンドを参照してください。

  • 更新内容がフィールドの一意制約に違反する場合は、フィールド値を更新できません。一意制約のあるフィールドに重複値を指定して更新しようとすると、SQLCODE -120 エラーが返されます。このエラーは、フィールドに UNIQUE データ制約がある場合、またはフィールドのグループに複数フィールドでの一意制約が適用されている場合に返されます。SQLCODE -120 %msg 文字列には、一意制約に違反するフィールドおよび値が含まれています。例えば、“[テーブル 'SQLUser.WordTest'、制約 'WORDTEST_UNIQUE3'、フィールド Firstword="Pronto":一意性チェックに失敗しました]” になります。

  • 数値はキャノニック形式で挿入されますが、先頭と末尾の 0 や、先頭の複数の符号を付けて指定できます。ただし、SQL では、2 つの連続したマイナス記号は、1 行コメント文字として解析されます。したがって、先頭に 2 つの連続したマイナス記号を付けて数値を指定しようとすると、SQLCODE -12 エラーになります。

  • WHERE CURRENT OF 節を使用する場合は、現在のフィールド値を使用して新しい値を生成してフィールドを更新することはできません。例としては、SET Salary=Salary+100SET Name=UPPER(Name) が挙げられます。これを実行しようとすると、SQLCODE -69 エラーが返されます。SET <field> = <value expression> を WHERE CURRENT OF <cursor> で使用できません。

  • データ型が SERIAL のフィールドは、データの値が現在ない (NULL) 場合、または値が 0 の場合を除き、更新できません。更新しようとすると、SQLCODE -105 エラーが発生します。

  • 指定された行の 1 行の更新が外部キーの参照整合性に違反する場合 (かつ %NOCHECK が指定されていない場合)、UPDATE はいずれの行も更新せず、SQLCODE -124 エラーが発行されます。これは、NOCHECK キーワードを使用して外部キーを定義している場合には適用されません。

  • 非ストリーム・フィールドをストリーム・データで更新することはできません。これにより、以下に示すように SQLCODE -303 エラーが発生します。

リスト構造

Caché は、リスト構造のデータ型 %List (データ型クラス %Library.List) をサポートしています。これは圧縮バイナリ形式であり、Caché SQL で対応するネイティブなデータ型にマップしません。また、データ型 VARBINARY に対応しており、その MAXLEN の既定値は 32749 です。このため、ダイナミック SQL では、型 %List のプロパティ値を設定するときに、UPDATEINSERT も使用できません。詳細は、このドキュメントのリファレンスの "データ型" を参照してください。

ストリーム値

単一の UPDATE を使用して、ストリーム値フィールドを含む複数の行を更新することはできません。ストリーム・データ・フィールドは一度に 1 行ずつ更新する必要があります。

Stream フィールドをリテラル値で、または既存ストリーム・オブジェクトへのオブジェクト参照 (oref) で更新することができます。Caché は、このオブジェクトを開いて、更新するストリーム・フィールドにそのコンテンツをコピーします。

非ストリーム・フィールドをストリーム・フィールドのコンテンツで更新することはできません。これを実行すると、SQLCODE -303 エラー : “UPDATE 割り当てでストリーム・フィールドから非ストリーム・フィールド への暗黙の変換はありません” が返されて失敗します。文字列フィールドをストリーム・データで更新するには、以下の例のように、最初に SUBSTRING 関数を使用してストリーム・データの最初の n 文字を文字列に変換する必要があります。

UPDATE MyTable
     SET MyStringField=SUBSTRING(MyStreamField,1,2000)

計算フィールド

COMPUTECODE を指定して定義されているフィールドは、以下のように、UPDATE 処理の一部として値を再計算できます。.

  • COMPUTECODE:値は、計算されて INSERT 時に保存されます。UPDATE 時には変更されません。

  • COMPUTEONCHANGE の COMPUTECODE:値は、計算されて INSERT 時に保存されます。UPDATE 時には再計算されて保存されます。

  • DEFAULT と COMPUTEONCHANGE の COMPUTECODE:既定値は、INSERT 時に保存されます。値は、UPDATE 時に計算されて保存されます。

  • CALCULATED または TRANSIENT が指定された COMPUTECODE:このフィールドには値が格納されないので、値の UPDATE は実行できません。値は照会されるときに計算されます。ただし、計算済みフィールド内の値を更新しようとすると、Caché は指定された値に対する検証を実行し、値が無効であればエラーを発行します。値が有効である場合、Caché は更新操作を実行せず、SQLCODE エラーを発行せず、ROWCOUNT をインクリメントします。

ただし、UPDATE 処理の新しいフィールド値が既存のフィールド値と同じである場合、COMPUTEONCHANGE 計算フィールドは再計算されません。

ほとんどの場合、計算フィールドは読み取り専用として定義します。これによって、他のフィールド値が関与する計算の結果とされる値が、更新処理によって直接変更されないようになります。この場合、計算フィールドの値を上書きする UPDATE を使用しようとすると、SQLCODE -138 エラーが発生します。

ただし、1 つ (以上) のソース・フィールド値の更新を反映できるように、計算フィールド値を改訂することもできます。このような改訂は、指定されたソース・フィールドを更新した後に計算フィールド値を再計算する更新トリガを使用することで実行できます。例えば、Salary データ・フィールドの更新によって、Bonus 計算フィールドを再計算するトリガを起動したとします。この更新トリガによって Bonus が再計算され、Bonus が読み取り専用フィールドであっても正常に完了します。"CREATE TRIGGER" 文を参照してください。

FROM 節

UPDATE コマンドでは FROM キーワードを省略できます。更新するテーブル (またはビュー) を指定し、更新する行を WHERE 節を使用して選択するだけでもかまいません。

ただし、value-assignment-statement の後に FROM 節をオプションで含めることもできます。この FROM 節では、更新するレコードを特定するために使用される 1 つ以上のテーブルを指定します。この FROM 節は、通常は (常にではありませんが) 複数のテーブルを含む WHERE 節と共に使用します。FROM 節は複雑にすることができ、ANSI 結合構文を含めることができます。SELECT FROM 節でサポートされている構文はすべて、UPDATE FROM 節で許可されています。この UPDATE FROM 節は、Transact-SQL との機能的な互換性を提供します。

以下の例は、この FROM 節の使用法を示しています。ここでは、Retirees テーブル内に同じ EmpId がある場合、それらのレコードが Employees テーブルで更新されます。

UPDATE Employees AS Emp
     SET retired='Yes'
     FROM Retirees AS Rt
     WHERE Emp.EmpId = Rt.EmpId

UPDATE table-ref と FROM 節で同じテーブルを参照する場合には、参照するテーブルが文字どおり同じであることも、テーブルの 2 つのインスタンスの結合であることもあります。これは、テーブルのエイリアスの使用方法によって異なります。

  • 両方のテーブル参照にエイリアスがない場合には、両方とも同じテーブルを参照します。

      UPDATE table1 value-assignment FROM table1,table2   /* join of 2 tables */
    
  • 両方のテーブル参照に同じエイリアスがある場合には、両方とも同じテーブルを参照します。

      UPDATE table1 AS x value-assignment FROM table1 AS x,table2   /* join of 2 tables */
    
  • 両方のテーブル参照にエイリアスがあり、それぞれのエイリアスが異なる場合には、Caché は 2 つのテーブルのインスタンスの結合を実行します。

      UPDATE table1 AS x value-assignment FROM table1 AS y,table2   /* join of 3 tables */
    
  • 最初のテーブル参照にエイリアスがあり、2 番目にはない場合には、Caché は 2 つのテーブルのインスタンスの結合を実行します。

      UPDATE table1 AS x value-assignment FROM table1,table2   /* join of 3 tables */
    
  • 最初のテーブル参照にエイリアスがなく、2 番目にはエイリアスのあるテーブルへの単一の参照がある場合には、両方とも同じテーブルを参照し、このテーブルには指定されたエイリアスがあります。

      UPDATE table1 value-assignment FROM table1 AS x,table2   /* join of 2 tables */
    
  • 最初のテーブル参照にエイリアスがなく、2 番目にはテーブルへの複数の参照がある場合には、それぞれのエイリアスされたインスタンスは個別のテーブルと見なされ、それらのテーブルの結合が実行されます。

      UPDATE table1 value-assignment FROM table1,table1 AS x,table2        /* join of 3 tables */
      UPDATE table1 value-assignment FROM table1 AS x,table1 AS y,table2   /* join of 4 tables */
    
    

制限引数

restriction 引数を使用するには、現在のネームスペースに対応する admin-privilege が必要となります。詳細は "GRANT" を参照してください。

restriction 引数を指定すると、以下のように処理を制限します。

  • %NOCHECK — 外部キーの参照整合性チェックは実行されません。データ型、最大長、データ制約などの検証条件に関して、列データの検証も実行されません。ビューを介して UPDATE を実行する場合、ビューの WITH CHECK OPTION 検証は実行されません。

    Note:

    %NOCHECK を使用すると、無効なデータが発生する場合があるため、この制約引数の使用は、信頼性の高いデータ・ソースから挿入または更新を一括で実行する場合に限定してください。

  • %NOLOCK — UPDATE 時に行をロックしません。単独のユーザ/処理がデータベースを更新する際にのみ使用します。

  • %NOINDEX — インデックス・マップは UPDATE 処理中には設定されません。

  • %NOTRIGGER — ベース・テーブル・トリガは UPDATE 処理中にはかかりません (実行されません)。BEFORE トリガおよび AFTER トリガのどちらも実行されません。

複数の restriction 引数を順不同で指定できます。複数の引数は、空白で区切られます。

参照整合性

%NOCHECK を指定しない場合、Caché ではシステム構成設定を使用して外部キーの参照整合性チェックを実行するかどうかが決まります。システムの既定値は以下のように設定できます。

  • $SYSTEM.SQL.SetFilerRefIntegrity()Opens in a new tab メソッド呼び出し。

  • 管理ポータルに進み、システム, 構成, 一般SQL設定 を選択します。[INSERT、UPDATE、DALETE の外部キーについて参照整合性チェックを実行する] の現在の設定を表示して編集します。既定は “はい” です。この設定を変更すると、変更後に開始される新しいプロセスは、新しい設定になります。

この設定は、NOCHECK キーワードを使用して定義した外部キーには適用されません。

UPDATE 処理中は、更新するフィールド値を持つ外部キーの参照があるたびに、参照するテーブルの古い (更新前の) 参照行と新しい (更新後の) 参照行の両方で共有ロックを取得します。これらの行は参照整合性チェックと行の更新が完了するまでロックされます。その後、ロックは解除されます (トランザクションの終了までロック状態が継続することはありません)。このロックによって、参照整合性チェックから更新操作の完了までの間、参照先の行が変更されないようになります。古い行をロックすることにより、参照される行は、UPDATE のロールバックがあってもそれより前に変更されることがなくなります。新しい行をロックすることにより、参照整合性チェックから更新操作の完了までの間、参照先の行が変更されないようになります。

CASCADE、SET NULL、または SET DEFAULT で定義された外部キー・フィールドに対して、%NOLOCK を指定して UPDATE 操作を実行した場合は、対応する参照アクションが %NOLOCK によって実行されて外部キー・テーブルが変更されます。

アトミック性

既定では、UPDATEINSERTDELETE、および TRUNCATE TABLE はアトミック処理として実行されます。UPDATE は、正常に完了するか、すべての操作がロールバックされるかのいずれかです。指定した行のいずれかを更新できない場合、指定した行は 1 行も更新できずにデータベースは UPDATE を発行する前の状態に戻ります。

現在のプロセスに対するこの既定は、SET TRANSACTION %COMMITMODE を呼び出すことによって SQL 内で変更できます。現在のプロセスに対するこの既定は、SetAutoCommit()Opens in a new tab メソッドを呼び出すことによって ObjectScript 内で変更できます。以下のオプションを使用できます。

  • IMPLICIT または 1 (自動コミットがオン) — 上記のように、これが既定の動作です。UPDATE ごとに個別のトランザクションが構成されます。

  • EXPLICIT または 2 (自動コミットがオフ) — 進行中のトランザクションがない場合は、UPDATE コマンドによってトランザクションは自動的に開始されます。ただし、COMMIT または ROLLBACK で明示的にトランザクションを終了する必要があります。EXPLICIT モードでは、トランザクションあたりのデータベース操作の数は、ユーザ定義です。

  • NONE または 0 (トランザクションなしを自動化しない) — UPDATE を呼び出してもトランザクションは開始されません。UPDATE 操作の失敗により、行の一部が更新されたり更新されなかったりすることで、データベースが整合性のない状態になる可能性があります。このモードでトランザクションのサポートを提供するには、START TRANSACTION を使用してトランザクションを開始し、COMMIT または ROLLBACK を使用してトランザクションを終了する必要があります。

現在のプロセスのアトミック性設定を確認するには、以下の ObjectScript の例のように、GetAutoCommit()Opens in a new tab メソッドを使用します。

  DO $SYSTEM.SQL.SetAutoCommit($RANDOM(3))
  SET x=$SYSTEM.SQL.GetAutoCommit()
  IF x=1 {
    WRITE "Default atomicity behavior",!
    WRITE "automatic commit or rollback" }
  ELSEIF x=0 {
    WRITE "No transaction initiated, no atomicity:",!
    WRITE "failed DELETE can leave database inconsistent",!
    WRITE "rollback is not supported" }
  ELSE { WRITE "Explicit commit or rollback required" }

トランザクションでのロック

%NOLOCK を指定しない場合、INSERTUPDATE、および DELETE 操作時に自動的にレコードに標準のロックがかかります。影響を受ける各レコード (行) は、現在のトランザクションが継続している間はロックされます。

既定のロックしきい値は、テーブルごとに 1000 ロックです。つまり、トランザクションの間にテーブルで 1000 件を超えるレコードを更新すると、ロックのしきい値に到達し、Caché は自動的にロック・レベルをレコード・ロックからテーブル・ロックに上げます。これによってトランザクション時に、ロック・テーブルをオーバーフローすることなく、大規模な更新を実行できます。

Caché は、以下の 2 つのロック・エスカレーション策のどちらかを適用します。

  • “E” タイプのロック・エスカレーション:Caché は、次のことに該当する場合にこのタイプのロック・エスカレーションを使用します。(1) クラスで %CacheStorage が使用されている (これは、管理ポータルの SQL スキーマ表示で [カタログの詳細] から判断できます)。(2) クラスで、IDKey インデックスが指定されていないか、単一プロパティの IDKey インデックスが指定されている。“E” タイプのロック・エスカレーションについては、"Caché ObjectScript リファレンス" の LOCK コマンドで説明されています。

  • 従来の SQL ロック・エスカレーション:クラスで “E” タイプのロック・エスカレーションが使用されない理由は、マルチプロパティの IDKey インデックスの存在にあると考えられます。この場合は、%Save ごとにロック・カウンタがインクリメントされます。つまり、トランザクション内の単一オブジェクトを 1001 回保存を行うと、Caché はロックのエスカレーションを試みます。

どちらのロック・エスカレーション策の場合も、$SYSTEM.SQL.GetLockThreshold()Opens in a new tab メソッドを使用して、現在のシステム全体用ロックしきい値を決定できます。既定値は 1,000 です。このシステム全体のロックしきい値は、以下の方法を使用して設定できます。

  • $SYSTEM.SQL.SetLockThreshold()Opens in a new tab メソッドを使用します。

  • 管理ポータルを使用する。システム, 構成, 一般SQL設定 に移動します。[ロックしきい値] の現在の設定を表示して編集します。既定は 1000 ロックです。この設定を変更すると、変更後に開始される新しいプロセスは、新しい設定になります。

ロックしきい値を変更するには、%Admin Manage Resource の USE 許可が必要です。Caché は、ロックしきい値の変更を現在のプロセスすべてに即座に適用します。

結果として、自動ロック・エスカレーションでは、デッドロックの状況が起こる可能性があります。つまり、テーブル・ロックへのエスカレーションを試みたときに、テーブル内のレコード・ロックを保持する別プロセスとの競合が起こる可能性があります。これを避けるための方策としては、次のいくつかが考えられます。 (1) ロック・エスカレーションがトランザクション内で起こる可能性が低くなるように、ロック・エスカレーションのしきい値を上げる。(2) ロック・エスカレーションが即座に起こるように、ロック・エスカレーションのしきい値を大幅に下げる。これにより、別プロセスが同一テーブル内のレコードをロックする機会が少なくなります。(3) トランザクションが継続している間はテーブル・ロックを適用し、レコード・ロックは実行しない。これは、LOCK TABLE、UNLOCK TABLE (テーブル・ロックがトランザクションの終了まで持続するよう、IMMEDIATE キーワードはなし) の順に指定することで、トランザクション開始時に実行できます。その後、%NOLOCK オプションを使用して更新を実行します。

自動ロック・エスカレーションは、ロック・テーブルのオーバーフローを防ぐことを目的としています。ただし、大量の更新などを実行したために <LOCKTABLEFULL> エラーが発生した場合は、UPDATE によって SQLCODE -110 エラーが発行されます。

トランザクションでのロックの詳細は、"Caché SQL の使用法" の “データベースの変更” の章にある "トランザクション処理" を参照してください。

行レベル・セキュリティ

Caché の行レベル・セキュリティにより、UPDATE を使用して、セキュリティでアクセスが許可されるすべての行を変更できるようになります。セキュリティにより今後のアクセスが許可されない行が UPDATE によって作成された場合でも、行の更新が可能になります。UPDATE により行への今後の SELECT アクセスが妨げられないようにするには、WITH CHECK OPTION を持つビューで UPDATE を実行することをお勧めします。詳細は、"CREATE VIEW" を参照してください。

ROWVERSION カウンタのインクリメント

テーブルにデータ型 ROWVERSION のフィールドが存在する場合、行の更新を実行すると、このフィールドの整数値が自動的に更新されます。ROWVERSION フィールドは、ネームスペース全体の行バージョン・カウンタの連続した次の整数を取得します。ROWVERSION フィールドに更新値を指定すると、SQLCODE -138 エラーが返されます。

SERIAL (%Counter) カウンタのインクリメント

UPDATE 操作は、SERIAL (%Library.Counter) カウンタ・フィールドの値に影響しません。ただし、INSERT OR UPDATE を使用して実行される更新では、SERIAL フィールドに対する後続の挿入操作で整数シーケンスのスキップが発生します。詳細は "IINSERT OR UPDATE" を参照してください。

このセクションの例では、SQLUser.MyStudents テーブルを更新しています。以下の例では、SQLUser.MyStudents テーブルを作成し、そのテーブルにデータを生成しています。この例を繰り返し実行すると、重複データのあるレコードが累積するため、TRUNCATE TABLE を使用して、INSERT を呼び出す前に古いデータを削除します。この例は、UPDATE の例を呼び出す前に実行します。

CreateStudentTable
  ZNSPACE "Samples"
    SET stuDDL=5
    SET stuDDL(1)="CREATE TABLE SQLUser.MyStudents ("
    SET stuDDL(2)="StudentName VARCHAR(32),StudentDOB DATE,"
    SET stuDDL(3)="StudentAge INTEGER COMPUTECODE {SET {StudentAge}="
    SET stuDDL(4)="$PIECE(($PIECE($H,"","",1)-{StudentDOB})/365,""."",1)} CALCULATED,"
    SET stuDDL(5)="Q1Grade CHAR,Q2Grade CHAR,Q3Grade CHAR,FinalGrade VARCHAR(2))"
  SET tStatement = ##class(%SQL.Statement).%New(0,"Sample")
  SET qStatus = tStatement.%Prepare(.stuDDL)
    IF qStatus'=1 {WRITE "DDL %Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET rtn = tStatement.%Execute()
  IF rtn.%SQLCODE=0 {WRITE !,"Table Create successful"}
  ELSEIF rtn.%SQLCODE=-201 {WRITE "Table already exists, SQLCODE=",rtn.%SQLCODE,!}  
  ELSE {WRITE !,"table create failed, SQLCODE=",rtn.%SQLCODE,!
        WRITE rtn.%Message,! }
RemoveOldData
  SET clearit="TRUNCATE TABLE SQLUser.MyStudents"
  SET qStatus = tStatement.%Prepare(clearit)
   IF qStatus'=1 {WRITE "Truncate %Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET truncrtn = tStatement.%Execute()
  IF truncrtn.%SQLCODE=0 {WRITE !,"Table old data removed",!}
  ELSEIF truncrtn.%SQLCODE=100 {WRITE !,"no data to be removed",!}
  ELSE {WRITE !,"truncate failed, SQLCODE=",truncrtn.%SQLCODE," ",truncrtn.%Message,! }
PopulateStudentTable
  SET studentpop=2
  SET studentpop(1)="INSERT INTO SQLUser.MyStudents (StudentName,StudentDOB) "
  SET studentpop(2)="SELECT Name,DOB FROM Sample.Person WHERE Age <= '21'"
  SET qStatus = tStatement.%Prepare(.studentpop)
    IF qStatus'=1 {WRITE "Populate %Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET poprtn = tStatement.%Execute()
  IF poprtn.%SQLCODE=0 {WRITE !,"Table Populate successful",!
        WRITE poprtn.%ROWCOUNT," rows inserted"}
  ELSE {WRITE !,"table populate failed, SQLCODE=",poprtn.%SQLCODE,!
        WRITE poprtn.%Message }

以下のクエリを使用して、これらの例の結果を表示できます。

SELECT %ID,* FROM SQLUser.MyStudents ORDER BY StudentAge,%ID

以下の UPDATE の例のいくつかは、他の UPDATE の例で設定されるフィールド値に依存しているため、指定された順序で実行する必要があります。

以下のダイナミック SQL の例では、SET field=value UPDATE は、選択したレコード内の指定したフィールドを変更します。MyStudents テーブルでは、7 歳未満の児童には成績が指定されていません。

  ZNSPACE "Samples"
    SET studentupdate=3
    SET studentupdate(1)="UPDATE SQLUser.MyStudents "
    SET studentupdate(2)="SET FinalGrade='NA' "
    SET studentupdate(3)="WHERE StudentAge <= 6"
  SET tStatement = ##class(%SQL.Statement).%New(0,"Sample")
  SET qStatus = tStatement.%Prepare(.studentupdate)
    IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET uprtn = tStatement.%Execute()
  IF uprtn.%SQLCODE=0 {WRITE !,"Table Update successful"
                       WRITE !,"Rows updated=",uprtn.%ROWCOUNT," Final RowID=",uprtn.%ROWID}
  ELSE {WRITE !,"Table update failed, SQLCODE=",uprtn.%SQLCODE," ",uprtn.%Message }

以下のカーソル・ベースの埋め込み SQL の例では、SET field1=value1,field2=value2 UPDATE は、選択したレコード内のいくつかのフィールドを変更します。MyStudents テーブルで、これは指定した学生レコードを Q1 と Q2 の成績で更新します。

  #SQLCompile Path=Sample
  NEW SQLCODE,%ROWCOUNT,%ROWID
  &sql(DECLARE StuCursor CURSOR FOR 
        SELECT * FROM MyStudents
        WHERE %ID IN(10,12,14,16,18,20,22,24) AND StudentAge > 6)
   &sql(OPEN StuCursor)
   FOR { &sql(FETCH StuCursor)
        QUIT:SQLCODE 
        &sql(Update MyStudents SET Q1Grade='A',Q2Grade='A'
       WHERE CURRENT OF StuCursor)
    IF SQLCODE=0 {
    WRITE !,"Table Update successful"
    WRITE !,"Row count=",%ROWCOUNT," RowID=",%ROWID }
    ELSE {
    WRITE !,"Table Update failed, SQLCODE=",SQLCODE }
    }
    &sql(CLOSE StuCursor)

以下のダイナミック SQL の例では、field-list VALUES value-list UPDATE は、選択したレコード内のいくつかのフィールドの値を変更します。MyStudents テーブルでは、最終成績を受け取らない児童は、四半期成績も受け取りません。

  ZNSPACE "Samples"
    SET studentupdate=3
    SET studentupdate(1)="UPDATE SQLUser.MyStudents "
    SET studentupdate(2)="(Q1Grade,Q2Grade,Q3Grade) VALUES ('x','x','x') "
    SET studentupdate(3)="WHERE FinalGrade='NA'"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET qStatus = tStatement.%Prepare(.studentupdate)
   IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET uprtn = tStatement.%Execute()
  IF uprtn.%SQLCODE=0 {WRITE !,"Table Update successful"
                       WRITE !,"Rows updated=",uprtn.%ROWCOUNT," Final RowID=",uprtn.%ROWID}
  ELSE {WRITE !,"Table Update failed, SQLCODE=",uprtn.%SQLCODE," ",uprtn.%Message,! }

以下のダイナミック SQL の例では、VALUES value-list UPDATE は、選択したレコード内のすべてのフィールド値を変更します。この構文では、レコード内のすべてのフィールドの値を指定する必要があります。MyStudents テーブルでは、数人の児童が学校を退学しています。そのレコード ID と名前は保持され、名前に WITHDRAWN が付加されます。その他すべてのデータは削除され、DOB フィールドは退学の日付用に使用されます。

  ZNSPACE "Samples"
    SET studentupdate=4
    SET studentupdate(1)="UPDATE SQLUser.MyStudents "
    SET studentupdate(2)="VALUES (StudentName||' WITHDRAWN',"
    SET studentupdate(3)="$PIECE($HOROLOG,',',1),00,'-','-','-','XX') "
    SET studentupdate(4)="WHERE %ID IN(7,10,22)"
  SET tStatement = ##class(%SQL.Statement).%New()
  SET qStatus = tStatement.%Prepare(.studentupdate)
    IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET uprtn = tStatement.%Execute()
  IF uprtn.%SQLCODE=0 {WRITE !,"Table Update successful"
                       WRITE !,"Rows updated=",uprtn.%ROWCOUNT," Final RowID=",uprtn.%ROWID}
  ELSE {WRITE !,"Table Update failed, SQLCODE=",uprtn.%SQLCODE," ",uprtn.%Message,! }

以下のダイナミック SQL の例では、subquery UPDATE はレコードの選択にサブクエリを使用しています。次にそれらのレコードを SET field=value 構文を使用して変更しています。SQLUser.MyStudents にある誕生日から StudentAge を計算する方法により、1 歳未満の児童の年齢は <Null> と算出されており、誕生日が NULL となっている児童は算出年齢がかなり高くなっています。ここで StudentName フィールドには、後で誕生日を確認するためにフラグが付けられています。

  ZNSPACE "Samples"
    SET studentupdate=3
    SET studentupdate(1)="UPDATE (SELECT StudentName FROM SQLUser.MyStudents "
    SET studentupdate(2)="WHERE StudentAge IS NULL OR StudentAge > 21) "
    SET studentupdate(3)="SET StudentName = StudentName||' *** CHECK DOB' "
  SET tStatement = ##class(%SQL.Statement).%New(0,"Sample")
  SET qStatus = tStatement.%Prepare(.studentupdate)
    IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET uprtn = tStatement.%Execute()
  IF uprtn.%SQLCODE=0 {WRITE !,"Table Update successful"
                       WRITE !,"Rows updated=",uprtn.%ROWCOUNT," Final RowID=",uprtn.%ROWID}
  ELSE {WRITE !,"Table Update failed, SQLCODE=",uprtn.%SQLCODE," ",uprtn.%Message,! }

以下の埋め込み SQL の例では、VALUES :array() UPDATE は、選択したレコード内の配列の列番号によって指定されたフィールド値を変更します。VALUES :array() の更新は、埋め込み SQL でのみ実行できます。この構文では、各値を DDL 列番号 (RowId 列を含む) で指定する必要があります。MyStudents テーブルでは、4 歳以上 6 歳以下の児童には、‘P’ (‘Present (在籍)’ を示す) がその Q1Grade (列 5) および Q2Grade (列 6) のフィールドに指定されます。他のすべてのレコード・データは、変更されません。

  ZNSPACE "Samples"
  SET arry(5)="P"
  SET arry(6)="P"
  &sql(UPDATE SQLUser.MyStudents VALUES :arry() 
       WHERE FinalGrade='NA' AND StudentAge > 3)
  IF SQLCODE=0 {WRITE "Table Update successful",!
                WRITE "Rows updated=",%ROWCOUNT," Final RowID=",%ROWID }
  ELSE {WRITE "Table Update failed, SQLCODE=",SQLCODE,! }

関連項目

FeedbackOpens in a new tab