1

まず、ここに投稿する言い訳です。通常、十分な時間をかけて検索することで、状況に対する答えを見つけることができるはずだと感じています...これまでのところ、検索とテストに約 5 時間かかりましたが、結果を説明することはできません。私は得ています。私は現時点でちょっと頭がおかしいです。あなたの誰かが私を助けることができれば、それは非常に高く評価されます.

状況

以下はすべて既存のコードですが、動作を最適化するのに忙しいです。

私は 3 つのテーブルで作業しています。

  • 最初のテーブルには項目が含まれ、
  • 2 番目のテーブルには、すべての項目のプロパティ フィールドが含まれており、その他のフィールドには名前とデフォルト値があります。
  • 3 番目のテーブルには、id によってアイテムにリンクされ、フィールド id によってフィールドにリンクされた、プロパティ フィールドのフィールド値が含まれます。

アイデアは、値を持つ各アイテムのすべてのフィールドを取得することです。フィールドとアイテムの値の行が値テーブルに存在しない場合は、既定値を使用する必要があります。

これはすべて 1 つのクエリで発生する必要があります。

私の前の人は、フィールドが追加されるたびに、デフォルト値フィールドがすべてのアイテムの値テーブルに挿入されるようにすることで、この問題を「修正」しました。データベース テーブルに 10,000 を超えるアイテムと 10 を超えるフィールドを含めることができる場合、これはもちろん間違った方法です。

私のテストケース

このシステムに 2 年以上取り組んできた私は、ついにこの問題を修正する時間を得ることができました。動作中のシステムでの通常のテストでは、常に期待どおりの結果が得られませんでした。これは、社内テスト システムの現在の状態です。

  • 277項目の行
  • 3 フィールド行
  • 824 値行

これは、クリーンアップのために一度だけクエリを実行して、システム上に存在しなくなったアイテムとフィールドの値をクリーンアップした後です (はい、この部分はバグだらけです)。

DELETE FROM values WHERE item_id NOT IN (SELECT id FROM items) OR field_id NOT IN (SELECT id FROM fields);

元のテーブルにはより多くのフィールドが含まれているため、最小限の要件でダミーシステムも作成しました。

-- table a represents items
CREATE TABLE IF NOT EXISTS `a` (
  `id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `a` (`id`) VALUES (1),(2),(3);

-- table b represents fields
CREATE TABLE IF NOT EXISTS `b` (
  `id` int(11) NOT NULL,
  `default` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `b` (`id`, `default`) VALUES
(1, 4),
(2, 5),
(3, 6),
(4, 11),
(5, 12);

-- table c represents values
CREATE TABLE IF NOT EXISTS `c` (
  `id` int(11) NOT NULL,
  `a_id` int(11) NOT NULL,
  `b_id` int(11) NOT NULL,
  `value` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `c` (`id`, `a_id`, `b_id`, `value`) VALUES
(1, 1, 1, 7),
(2, 1, 2, 8),
(3, 2, 3, 9),
(4, 2, 1, 7),
(5, 3, 2, 8),
(6, 3, 3, 9),
(7, 1, 5, 13);

期待される結果は 831 行 (277 項目 * 3 フィールド)である必要があり、値テーブルで使用できない項目/フィールドの組み合わせには、値テーブルの値ではなくフィールドの既定値を入力する必要があります。

調査結果を確認するために作成したダミー システムで試した成功したテスト ケース SQL は、期待どおりの結果を返します。

SELECT  a.id,
    b.id, 
    IF(c.value IS NOT NULL, c.value, b.default) as t_value
FROM a
join b
LEFT JOIN c on c.a_id = a.id AND c.b_id = b.id

これは、15 行 (3 a (アイテム) x 5 b (フィールド)) を返し、期待されるすべてのデータを含みます。

社内テスト システムのクエリを変更すると、機能するはずでした。これは私が送ったSQLです:

SELECT items.id AS item_id, fields.id AS field_id, IF(values.value IS NULL, fields.default_value, value.value) AS field_value
FROM        items
JOIN        fields
LEFT JOIN   values ON values.item_id = item.id AND values.field_id = fields.id

...しかし、予想される831行ではなく1104行が返されます。テーブルから不正確なデータが削除され、考慮されていない余分なフィールドはSQLでまったく使用されず、さらに抽象テストによりクエリの概念が証明されました。現実的な状況だけが失敗し続けます。

誰かが私の間違いを指摘できれば、それは大歓迎です。ここにあるテーブル名は変更されていますが、リクエストがあれば、問題の社内テスト システム テーブルの部分的なダンプを投稿することもできます。ただし、上記の例は正確なはずです。

4

1 に答える 1

1

一意性をチェックしたという言及はありませんでした。「余分な行」が重複している可能性があります。

SELECT a.id FROM a GROUP BY a.id HAVING COUNT(1) > 1 ;

SELECT b.id FROM b GROUP BY b.id HAVING COUNT(1) > 1 ;

SELECT c.a_id, c.b_id FROM c GROUP BY c.a_id, c.b_id HAVING COUNT(1) > 1 ;

SELECT s.a_id, s.b_id
  FROM
       (
        your query here
       ) s
 GROUP BY s.a_id, s.b_id 
HAVING COUNT(1) > 1 ;

とのid列は一意で null でないことが期待されます。ab

(a_id,b_id)また、 incが一意で null でないことも期待できます。一意のインデックスを作成するか、UNIQUE KEY 制約を宣言すると、データベースはこの制約を適用できます。

ALTER TABLE c ADD CONSTRAINT c_ux UNIQUE KEY (a_id,b_id);

-また-

CREATE UNIQUE INDEX c_ux ON c (a_id, b_id); 

一意性の保証がない場合、クエリが「重複」a.idb.idペアを返す可能性があります。

クエリに追加された " GROUP BY" 句を使用して重複を排除することもできますが、それは実際にはもっと厄介なようです。(特定のアイテムと特定のフィールドが与えられた場合、何個の異なる値を格納できるようにしますか? また、値を元に戻すとき、実際にはどの値を返したいですか?)


id通常、これらの各テーブルの列は PRIMARY KEY として定義できると予想されます。

ALTER TABLE a ADD PRIMARY KEY (id);
ALTER TABLE b ADD PRIMARY KEY (id);
ALTER TABLE c ADD PRIMARY KEY (id);

また、(InnnoDB テーブルを使用して) 外部キーが定義されていることも期待できます。

ALTER TABLE c ADD CONSTRAINT FK_c_a (a_id) REFERENCES a (id) ;
ALTER TABLE c ADD CONSTRAINT FK_c_b (b_id) REFERENCES b (id) ;

私の好みはCROSS、a と b の間の結合にキーワードを含めることですが、クエリの動作には何の影響もありません。これは、ON 句の欠如が意図的であり、デカルト積を意図していたことを文書化するのに役立ちます。


アップデート:

このような EAV モデルは、現在の値だけでなく以前の値も保持するように設計されている場合があります。その場合、通常、現在の値を把握するために使用できる「有効日」および/または「置き換え日」および/または単純な「アクティブ」フラグがあります。そのため、重複は実装が不適切なためジャンクである可能性があり、「重複」は意図的なものである可能性があります。

その場合、一意のキーは次のようになります(a_id, b_id, effective_date)

EAV モデルで履歴 (「時間」) 値を処理するためのクエリはかなり複雑になる可能性があり、気まぐれな人向けではありません。しかし、それは可能です。

もう 1 つの可能性は、一部のフィールドが「多値」であることを意図しているということです。つまり、エンティティの繰り返し属性です。たとえば、アイテムには複数の「キーワード」または「タグ」値がある場合があります。「ホビット」には、「高フレーム レート」、「ファンタジー」、「耐え難い」などの「タグ」値が含まれる場合があります。しかし、データベースの設計を理解していなければ、自分がどこにいるのかを実際に判断することはできません.

しかし、データベースがフィールドの単一の「現在の」値のみを保持するように設計されている場合 (履歴値を表示する機能がない場合)、重複した(a_id,b_id)行が潜在的に (おそらく) ジャンクであることに同意します。何かを削除し始める前に、おそらくテーブル内のすべての行のコピーを (別の「保存」テーブルに) 保存します。

于 2013-01-18T13:59:48.640 に答える