列挙型とは何ですか? enum は E.164 Number URI Mapping の略称です。この略語の背後には、最適かつ最も安価なルーティングを介して、世界中のどこにいても同じ電話番号を使用できるという素晴らしいアイデアが隠されています。 ENUM番号もドメイン名と同じように登録できます。
プロジェクトを開発するとき、通常、支払い保留中、支払い済み、クローズ済み、返金済みなどの注文のステータスなど、いくつかのステータス フィールドが表示されます。私が以前に取り組んだプロジェクトでは、これらのステータスはデータベースに数値として保存されていました。それからphpで マッピング テーブルを維持するためにコード内で定数が使用されます。例:
const STATUS_PENDING = 0; const STATUS_PAID = 1; const STATUS_CLOSED = 2; const STATUS_REFUNDED = 3;
しかし、実際に使用すると、さまざまな理由 (追跡バグ、一時的な統計要件など) により、それほど簡単ではないことがわかります。 ) 多くの場合、mysql サーバーにログインする必要があります。手動で SQL を実行します。 クエリでは、多くのテーブルにステータス フィールドがあるため、PHP コード内のマッピング関係に従って SQL を記述する必要があります。注意しないと、異なるテーブルのステータス番号を混同し、大きな問題が発生する可能性があります。
そこで、新しいプロジェクトにさまざまな状態を保存するために mysql の enum 型を使用することを計画しました。使用中に、 enum 型テーブルへの変更 (非 enum 型フィールドへの変更も含む) はエラーになります
[Doctrine\DBAL\DBALException] Unknown database type enum requested, Doctrine\DBAL\Platforms\MySQL57Platform may not support it.
検索した結果、doctrine は mysql の enum をサポートしていないことがわかりました。この記事には enum の 3 つの欠点がリストされています:
新しい enum。 value テーブル全体を再構築する必要があります。データの量が多い場合は、数時間かかる場合があります。
enum 値は、リテラル値のサイズではなく、テーブル構造の作成時に指定された順序でソートされます。
列挙値を検証するために mysql に依存する必要はありません。デフォルト設定で不正な値を挿入すると、最終的には null 値になります。
新しいプロジェクトの実際の状況によると、ステータスフィールドをソートする必要がある可能性は低いですが、あったとしても、テーブル構造を設計するときに順序を設定できるため、デメリット2は無視できます。 ; そしてデメリット3 デメリット 1 については、コードの仕様や挿入/更新前の検証などで回避できますが、テストが必要です。
テスト準備#
まずテーブルを作成します:
CREATE TABLE `enum_tests` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `status` enum('pending','success','closed') COLLATE utf8mb4_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
次に100Wのデータを挿入します:
$count = 1000000; $bulk = 1000; $data = []; foreach (['pending', 'success', 'closed'] as $status) { $data[$status] = []; for ($i = 0; $i < $bulk; $i++) { $data[$status][] = ['status' => $status]; } } for ($i = 0; $i < $count; $i += $bulk) { $status = array_random(['pending', 'success', 'closed']); EnumTest::insert($data[$status]);
テストプロセス#
テスト1#
列挙値リストの最後に返金される値を追加します
ALTER TABLE `enum_tests` CHANGE `status` `status` ENUM('pending','success','closed','refunded') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
出力:
Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0
結論:最後に列挙値を追加する場合、コストはほとんどかかりません。
テスト 2: #
追加したばかりの値を削除して返金します
ALTER TABLE `enum_tests` CHANGE `status` `status` ENUM('pending','success','closed') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
出力:
Query OK, 1000000 rows affected (5.93 sec) Records: 1000000 Duplicates: 0 Warnings: 0
結論: 未使用の列挙値を削除するには、依然としてフル テーブル スキャンが必要であり、コストは高くなりますが、まだ許容範囲内です。
テスト 3: #
値リストの末尾ではなく途中に返金を挿入します
ALTER TABLE `enum_tests` CHANGE `status` `status` ENUM('pending','success','refunded', 'closed') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
出力:
Query OK, 1000000 rows affected (6.00 sec) Records: 1000000 Duplicates: 0 Warnings: 0
結論: 新しい値を値リストの途中に追加します元の列挙値リストにはテーブル全体のスキャンが必要であり、更新のコストが高くなります。
テスト 4: #
値リストの真ん中の値を削除します
ALTER TABLE `enum_tests` CHANGE `status` `status` ENUM('pending','success','closed') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
出力:
Query OK, 1000000 rows affected (4.23 sec) Records: 1000000 Duplicates: 0 Warnings: 0
結論: フルテーブルスキャンが必要であり、コストが高くなります。
テスト 5: #
ステータス フィールドにインデックスを追加し、上記のテストを実行します
ALTER TABLE `enum_tests` ADD INDEX(`status`);
テスト 2 ~ 4 の消費時間が増加していることがわかります。これは、同時にインデックスも更新します。
結論: #
私の新しいプロジェクトでは、将来いくつかの状態が放棄されたとしても、新しい enum 値のみが表示されるため、enum 値のリストを調整する必要はありません。プロジェクト。 Type は状態を保存するためのデータ型として機能します。
関連する推奨事項:
enumデータ型のデフォルト値に関する混乱の解決について考える
以上がMySQL 列挙型インスタンスのテストの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。