ルールを厳格化する前に受け取り選択を補完する

MySQLBeginner
オンラインで実践に進む

はじめに

店の受け取り列はすでに拡張され、旧クライアント用の既定値は counter です。過去の注文には、まだ承認済みの選択を反映する必要があります。その後の注文も到着済みで、1 件は新列を省略し、1 件は明示的に garden を選びました。広範な補完は、この正当な新しい選択を上書きするおそれがあります。

backfill.sql と tighten.sql を用意し、過去の正確な承認済み対応表を適用して、両クライアントの契約を保ちます。前提は前の実習の概念と基本的な SELECT/WHERE、UPDATE です。JOIN、集計、一時ガード、トランザクションのパターンはここで学びます。この独立した Terminal の単元には、独自の拡張済みテーブル、データ、専用 MySQL があります。前の VM のファイル、接続、進捗は引き継ぎません。

更新するのは、承認済みの過去の ID に対する orders.pickup_code だけで、この列の型・既定値・NULL 許可・参照ルールを厳格化します。元のヘッダー、商品との関係、後の受け取り選択、承認・受け取り場所テーブル、実演テーブル、準備済みツールを保全してください。追加できるのは以下の練習注文 301 と 302 だけです。競合する承認書き込みのない、制御された保守時間で逐次リクエストを行います。オンライン移行、分散環境の調整、DDL の自動ロールバックは実装しません。作者の見積もりは読解と一般的なミスを含み、初心者の時間計測データはありません。

完全な承認済みの過去の選択だけを適用する

このステップでは、業務上の対応表を検証し、トランザクションによる補完を保存し、不正な対応表ではすべての注文値が不変であることを確かめます。

Terminal から始め、独立した作業領域に入ります。

cd /home/labex/project/pickup-desk
./shop status
./shop map
./shop points

過去の注文集合は legacy_orders で、ID は 101、102、103 です。承認済み対応表は、101 が north、102 が counter、103 が garden と指定しています。元の合計は 2100、3400、0 のままです。2 件の Sam の注文を顧客名で結合してはいけません。後の ID 201 と 202 は過去の集合の外なので、201 は counter、202 は garden を保ちます。商品明細も残す必要があります。

過去の識別情報ごとに承認行を数えます。COUNT(*) は行を数え、GROUP BY order_id は ID ごとのグループを作り、HAVING は集計後のグループを絞ります。<> は等しくないことを意味します。このクエリは、重複など、1 行でないグループを返します。

./db -e "SELECT order_id,COUNT(*) FROM approved_pickups GROUP BY order_id HAVING COUNT(*)<>1;"

空の結果が想定されます。ただし、行がなければグループもできないため、完全に欠けた ID は分かりません。LEFT JOIN は左の全行を保ち、一致がなければ右のフィールドを NULL にします。l と m は短いテーブル別名、ON は正確な注文 ID を関連付けます。IS NULL は右側に一致がない行を選びます。

./db -e "SELECT l.order_id FROM legacy_orders l LEFT JOIN approved_pickups m ON m.order_id=l.order_id WHERE m.order_id IS NULL;"

さらに、対応表の各コードが pickup_points に存在し、全 ID が legacy_orders に属することを要求します。この関係検査では、NULL のコードも不一致です。

./db -e "SELECT m.order_id,m.pickup_code FROM approved_pickups m LEFT JOIN pickup_points p ON p.code=m.pickup_code WHERE p.code IS NULL;"
./db -e "SELECT m.order_id FROM approved_pickups m LEFT JOIN legacy_orders l ON l.order_id=m.order_id WHERE l.order_id IS NULL;"

4 クエリはすべて空になるはずです。準備済みの実際の対応表は有効で読み取り専用です。空の監査出力は有用な根拠ですが、再利用可能な SQL ファイルも、有効な部分だけを盲目的に更新せず、不正な対応表を拒否する必要があります。

一時テーブルは 1 データベース接続に属し、接続終了時に消えます。小さなガードは整数ゼロだけを保存します。CHECK(errors=0) は非ゼロの件数、NOT NULL は件数の欠落を拒否します。INSERT ... SELECT COUNT(*) は違反数を挿入します。ガードの挿入が 1 つでも失敗すると、ネイティブバッチは業務 UPDATE の前に停止し、接続を閉じます。保留中の InnoDB データ変更はロールバックされます。ENGINE=InnoDB は、この一時テーブルに MySQL のトランザクション対応ストレージエンジンを選びます。一時テーブルそのものは、ロールバック可能な恒久スキーマ変更ではありません。各 apply や trial は新しい接続を開始するので、以前の一時テーブルを再利用する必要はありません。

前の実習の引用符付きヒアドキュメントで、完全なファイルを保存します。括弧内の duplicates という派生クエリは、1 行でないグループを、外側の COUNT が数えられる行にします。UPDATE JOIN は承認された正確な ID の注文だけを変え、全注文に既定値を割り当てるものではありません。COMMIT はガード成功後にだけ、データ変更を確定します。

cat > backfill.sql <<'SQL'
CREATE TEMPORARY TABLE map_guard (
    errors INT NOT NULL CHECK(errors=0)
) ENGINE=InnoDB;
START TRANSACTION;
INSERT INTO map_guard
SELECT COUNT(*) FROM legacy_orders l
LEFT JOIN approved_pickups m ON m.order_id=l.order_id
WHERE m.order_id IS NULL;
INSERT INTO map_guard
SELECT COUNT(*) FROM (
    SELECT order_id FROM approved_pickups
    GROUP BY order_id HAVING COUNT(*)<>1
) duplicates;
INSERT INTO map_guard
SELECT COUNT(*) FROM approved_pickups m
LEFT JOIN pickup_points p ON p.code=m.pickup_code
WHERE p.code IS NULL;
INSERT INTO map_guard
SELECT COUNT(*) FROM approved_pickups m
LEFT JOIN legacy_orders l ON l.order_id=m.order_id
WHERE l.order_id IS NULL;
UPDATE orders o
JOIN approved_pickups m ON m.order_id=o.order_id
SET o.pickup_code=m.pickup_code;
COMMIT;
SQL

まず、保存したファイルを使い捨てのネイティブデータベースで試します。trial は、異なる過去の ID と値を使い、隔離された移行用の権限で、ファイルを変更せず実行します。実際の SQL 終了ステータスと保全の比較結果を報告し、実際の対応表の編集や SQL の修復はしません。不正なケースで native_sql_status が非ゼロ、accepted が true になるのは、失敗が想定どおりで、すべての業務値が不変だった場合だけです。これは拒否を試すテストの成功であり、SQL 書き込みの成功ではありません。

./shop trial backfill.sql good
./shop trial backfill.sql missing
./shop trial backfill.sql duplicate
./shop trial backfill.sql unknown
./shop trial backfill.sql null
./shop trial backfill.sql extra

good は後の明示的な受け取り先を保ち、承認済みの全選択を適用します。各不正ケースはネイティブな CHECK エラー 3819 と、業務値の不変を報告します。順に、承認の削除、ID の重複、未知の場所、NULL、後の注文の上書きの試行です。常に失敗するファイルでは good に合格できません。受け入れられなければ、ネイティブな stderr と理由を読み、cat backfill.sql を確認し、ファイル全体を直します。テストを緑にするために準備済みツールや対応表を変えないでください。

同じ元のファイルを有効な実際の対応表に適用し、対象と保護対象の各注文を読みます。

./shop apply backfill.sql
./shop show 101
./shop show 102
./shop show 103
./shop show 201
./shop show 202

過去のコードは north、counter、garden です。後の 201 は counter、202 は garden のままで、メモと明細も残ります。既定値で埋まったスキーマができただけでは、過去の選択を証明しません。同じ不変の承認表で新しい接続からこのデータ補完だけを再実行すると、同じ選択になります。任意の 1 回限りの ALTER ファイルを再実行してよいという意味ではありません。

./shopp には ls を使い、ツール名を直します。構文エラーでは、保存した SQL を確認して置き換えてから再適用します。準備済みバッチは最初のネイティブエラーで止まって切断します。--force や、ガード失敗後も続ける対話型 SOURCE は使わないでください。この制御された保守時間は同時の承認編集を除外します。別々の監査クエリは、汎用的な同時移行のロック手順ではありません。

両方の購入クライアントを保ってルールを厳格化する

このステップでは、旧クライアントの列省略と新クライアントの明示的選択を使えるままにし、有効で NULL でない受け取り参照を強制します。

Terminal の /home/labex/project/pickup-desk で続けます。承認済み補完は完了しています。現在の定義を確認します。

./db -e "SHOW CREATE TABLE orders;"

列はまだ NULL を許し、明示的な counter の既定値があります。NOT NULL は明示的 NULL を拒否しますが、それだけで旧クライアント用の既定値を供給するものではありません。外部キーは pickup_code を実在する一意の pickup_points.code に結び、未知のコードを拒否します。RESTRICT は、使われている場所の削除・ID 変更を防ぎます。アプリのドロップダウン検査ではなく、実際のデータベースルールです。

MODIFY COLUMN は、意図する列定義全体を再指定します。NOT NULL を加える際、VARCHAR(16) と DEFAULT 'counter' の両方を保ちます。MODIFY で省いた属性が残るとは限りません。カンマで、両変更を 1 ALTER 文にまとめます。制約名はルールを識別しますが、同等の別名も受け入れられます。この DDL は、補完トランザクションの外に置きます。暗黙のコミット境界は前の単元で学びました。

cat > tighten.sql <<'SQL'
ALTER TABLE orders
MODIFY COLUMN pickup_code VARCHAR(16) NOT NULL DEFAULT 'counter',
ADD CONSTRAINT order_pickup_fk FOREIGN KEY(pickup_code)
REFERENCES pickup_points(code)
ON DELETE RESTRICT ON UPDATE RESTRICT;
SQL
./shop apply tighten.sql
./db -e "SHOW COLUMNS FROM orders;"
./db -e "SHOW CREATE TABLE orders;"

pickup_code が既定値 counter の NOT NULL であり、pickup_points(code) を参照して親の変更を制限することを読み取ります。既存値はすべて残ります。許可された練習注文 2 件を作ります。

./shop old 301 Mina 1600 MUG 1
./shop new 302 Pia 1900 BAG 2 north
./shop show 301
./shop show 302

実際の旧クライアントの行は counter、新クライアントの行は north を保存します。商品数量は 1 と 2 で、両トランザクションがコミットしました。旧クライアントは引き続き pickup_code を省略し、値を渡すよう密かに書き換えられたわけではありません。

次の 2 呼び出しは意図的な失敗で、非ゼロ終了が必要です。new-null はフィールドを省略せず、実際の SQL NULL を渡します。ネイティブエラー 1048 で拒否されるはずです。unknown を渡す通常の新クライアントは、ネイティブな外部キーエラー 1452 を受けるはずです。どちらもヘッダー・明細を残してはいけないため、同じ未使用 ID を使います。

./shop new-null 399 Invalid 100 MUG 1
./shop new 399 Invalid 100 MUG 1 unknown
./shop show 399
./shop status

show 399 のヘッダー・商品明細は空です。過去の 3 選択、後の 201/202、新しい 301/302 は正しいままです。ネイティブなデータベースエラーは、正常な空の購入結果でも接続問題でもありません。準備済みクライアントが失敗したトランザクションを閉じるため、VM リセットは不要です。既存の許可注文の読み取りを続けます。

最終成果は、再利用可能な補完・厳格化 SQL と、実際の正しい注文システムです。評価はこの実際の結果を読み、使い捨てのネイティブリソースで、保存した元の SQL と現在の定義を、正当な書き込みや禁止する親変更も含めて試します。実際の店で注文を作成したり、移行を適用したり、欠損値を修復したりはしません。

成功した tighten ファイルを誤って再実行すると、名前付き外部キーがすでにある場合があります。SHOW CREATE TABLE を調べ、有効な定義を保ちます。再実行可能にするために orders を削除・再作成しないでください。古いファイルにも、説明された前段階からの正しい 1 回限りの移行を保存しておく必要があります。保守時間の結果は、ロックなしの DDL、任意の配備ロールバック、継続的な本番稼働、管理者による偽装への耐性を証明しません。

まとめ

過去の正確な識別情報を検証し、不完全・あいまいな対応表を業務値の変更前に拒否し、後のクライアント選択を上書きせず、トランザクションによる補完を適用しました。別の厳格化 DDL では、旧クライアントの既定値を保ち、NULL でない有効な受け取り参照を強制しました。実際の両購入クライアントは引き続き動作し、不正な書き込みはネイティブに失敗します。