結合クエリもCashUpdateできるんです

サンプルソースは、
https://delphi-dev.sakura.ne.jp/FileBox/CashUpdateSample/
よりダウンロードできます。

## 結合クエリのCashUpdate

結合クエリでのCashUpdateを使った更新方法について書きます。
更新ができるテーブルは、当然1つだけです。
Delphi 11.3 
DBアクセス：FireDAC
DatabaseはSQLiteで確認しています。
他のコンポーエントやデータベースでも考え方は同じなので適用できると思います。

## テーブル

伝票明細(Meisai)、商品マスタ(Syouhin) (1:N関連)
（伝票明細を編集するイメージです）
```
CREATE TABLE MEISAI (
    ORDERNO    INTEGER NOT NULL,
    MEISAINO    INTEGER NOT NULL,
    SYOUHINCODE    INTEGER NOT NULL,
    TANKA    REAL DEFAULT 0,
    SURYO    REAL DEFAULT 0,
    BIKOU    TEXT DEFAULT '',
    PRIMARY KEY(ORDERNO,MEISAINO)
)
CREATE TABLE SYOUHIN (
    SYOUHINCODE    INTEGER NOT NULL,
    SYOUHINNAME    TEXT,
    SYOUHINTANKA    REAL,
    PRIMARY KEY(SYOUHINCODE)
)
```
## クエリ
```
SELECT
 M.ORDERNO
,M.MEISAINO
,M.SYOUHINCODE
,S.SYOUHINNAME
,S.SYOUHINTANKA
,M.TANKA　　　　　/* 更新対象 */
,M.SURYO　　　　　/* 更新対象 */
,M.BIKOU　　　　　/* 更新対象 */
FROM
  MEISAI M　　　/* 更新するテーブルはこの位置で無いといけない */
  INNER JOIN SYOUHIN S
  on (S.SYOUHINCODE = M.SYOUHINCODE)
where
 M.ORDERNO = :ORDERNO
order by
 M.MEISAINO
```
## FDQueryの設定

　前記クエリを設定後、フィールドエディタでフィールドの展開をする。
FDQueryのdfmファイル内容(関係ない部分は一部削除しています)
```
object QryList: TFDQuery
    OnCalcFields = QryListCalcFields
    CachedUpdates = True
    SQL.Strings = (
      select
       M.ORDERNO
      ,M.MEISAINO
      ,M.SYOUHINCODE
      ,S.SYOUHINNAME
      ,S.SYOUHINTANKA
      ,M.TANKA
      ,M.SURYO
      ,M.BIKOU
      from
        MEISAI M
        inner join SYOUHIN S
        on (S.SYOUHINCODE = M.SYOUHINCODE)
      where
       M.ORDERNO = :ORDERNO
      order by
       M.MEISAINO)
    object QryListORDERNO: TIntegerField
      FieldName = 'ORDERNO'
      ProviderFlags = [pfInWhere, pfInKey]
    end
    object QryListMEISAINO: TIntegerField
      FieldName = 'MEISAINO'
      ProviderFlags = [pfInWhere, pfInKey]
    end
    object QryListSYOUHINCODE: TIntegerField
      FieldName = 'SYOUHINCODE'
      ProviderFlags = [pfInUpdate]
    end
    object QryListSYOUHINNAME: TWideStringField
      FieldName = 'SYOUHINNAME'
      ProviderFlags = [pfHidden]
      ReadOnly = True
    end
    object QryListSYOUHINTANKA: TFloatField
      FieldName = 'SYOUHINTANKA'
      ProviderFlags = [pfHidden]
      ReadOnly = True
    end
    object QryListTANKA: TFloatField
      FieldName = 'TANKA'
      ProviderFlags = [pfInUpdate]
    end
    object QryListSURYO: TFloatField
      FieldName = 'SURYO'
      ProviderFlags = [pfInUpdate]
    end
    object QryListCalKINGAKU: TIntegerField
      FieldKind = fkCalculated
      FieldName = 'CalKINGAKU'
      Calculated = True
    end
    object QryListBIKOU: TWideStringField
      FieldName = 'BIKOU'
      ProviderFlags = [pfInUpdate]
    end
  end
```
## FDQueryの設定のポイント

ポイントは、ProviderFlagsになります。
更新するフィールドは、pfInUpdateをTrueにする。
更新時の、Where句に相当する部分は、pfInWhereをTrueにする。
join先のテーブルのフィールドは、pfHiddenをTrueにする。
テーブルの主キーのフィールドは、pfInKeyをTrueにする（ふつう勝手にTrueになる）

＊結合クエリとは関係ないが、BDEからFireDAC切り替え時、BDEでは設定しなくても問題なかったが、キー項目のProviderFlagsが、未設定だとエラーが出る。
エラー(例外:EFDDBEngineException)が出たらキー項目の下記の確認を。
ProviderFlags = [pfInWhere, pfInKey]
エラー内容は何パターンかあるようだ。
```
 メッセージ　[FireDAC][Phys][IB]-312.～～～～
```
```
 メッセージ　[FireDAC][DApt]-400.～～～～
```
など。
上記だけではわかりにくいかと思うのでサンプルを下記にアップ。データベースも含めています。
（含　LookUpフィールドサンプル。結合クエリを使わないもの）
明細の追加や削除は考慮して作っていません。上記確認のためなので諸々端折っています。

## 追記。。。。
今回のケースでは、
```
from
  MEISAI M　　　　/*更新するテーブルはこの位置で無いといけない*/
  inner join SYOUHIN S
  on (S.SYOUHINCODE = M.SYOUHINCODE)
```
の部分は、下記のようなLeft Outer Joinでも使えます
（仕様としては、商品マスタが無い商品を取り扱うというのは、NGですが）
```
from
  MEISAI M　　　　/*更新するテーブルはこの位置で無いといけない*/
  left outer join SYOUHIN S
  on (S.SYOUHINCODE = M.SYOUHINCODE)
```
でもＯＫ。
right outer join や full outer join は、当然ダメ。
ちなみにもともと、SQLiteは、right outer join / full outer join をサポートしていませんが。。。
他のデータベースをつかうときの注意点です。
ついでに。
たぶんやろうとしないと思うけど、複数のテーブル(今回では、MEISAIとSYOUHIN)を同時に更新もNGです。

## 追記その２。。。。
(結合クエリとLookupフィールドについて。私論)
 上記の処理では、SYOUHINCODEも変更するということが一般だと思う。
この場合、上記結合テーブルで行うと、SYOUHINCODEが変わったときの処理、コードでゴリゴリ書く必要が出てくる。そのように考えると、SYOUHINテーブルの引用は、Lookupフィールドを使ったほうが便利だと思う。
 ただ、読み込むMEISAIテーブルのデータが少ないケースはそれでよいが、件数が多いケースでは、Lookupフィールドの使用は、格段に処理時間がかかる。そういったケースではコードでゴリゴリ書くことに目をつぶって、結合テーブルを使用することもメリットが出ると思う。