サンプルソースは、
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)
でもOK。
right outer join や full outer join は、当然ダメ。
ちなみにもともと、SQLiteは、right outer join / full outer join をサポートしていませんが。。。
他のデータベースをつかうときの注意点です。
ついでに。
たぶんやろうとしないと思うけど、複数のテーブル(今回では、MEISAIとSYOUHIN)を同時に更新もNGです。
追記その2。。。。
(結合クエリとLookupフィールドについて。私論)
上記の処理では、SYOUHINCODEも変更するということが一般だと思う。
この場合、上記結合テーブルで行うと、SYOUHINCODEが変わったときの処理、コードでゴリゴリ書く必要が出てくる。そのように考えると、SYOUHINテーブルの引用は、Lookupフィールドを使ったほうが便利だと思う。
ただ、読み込むMEISAIテーブルのデータが少ないケースはそれでよいが、件数が多いケースでは、Lookupフィールドの使用は、格段に処理時間がかかる。そういったケースではコードでゴリゴリ書くことに目をつぶって、結合テーブルを使用することもメリットが出ると思う。