この種の問題に直面したとき、私は TDQD (テスト駆動型クエリ設計) を使用します。
テーブルに名前を付けると、誰にでも役立つことに注意してください。
パス 1
商品 1 と同じ属性を 1 つ以上持つ商品のリスト
SELECT a.ProductID, COUNT(*) AS matches
FROM EAV_Table AS a
JOIN EAV_Table AS b
ON a.Attribute = b.Attribute AND a.value = b.value
WHERE a.ProductID != 1
AND b.ProductID = 1
GROUP BY a.ProductID
これにより、カウントが 0 の製品は明らかにリストされません。これは問題ありません。
1 つ以上の属性が商品 1 と一致しない商品のリスト
SELECT c.ProductID, COUNT(*) AS matches
FROM EAV_Table AS c
JOIN EAV_Table AS d
ON c.Attribute = d.Attribute AND c.value != d.value
WHERE c.ProductID != 1
AND d.ProductID = 1
GROUP BY c.ProductID
これはまた、カウントが 0 の製品をリストしないため、より厄介です。
結果 — パス 1
製品が 2 番目のクエリにリストされていない最初のクエリからのすべての製品が必要です。これは、NOT EXISTS と相関サブクエリで表現できます。
SELECT a.ProductID, COUNT(*) AS matches
FROM EAV_Table AS a
JOIN EAV_Table AS b
ON a.Attribute = b.Attribute AND a.value = b.value
WHERE a.ProductID != 1
AND b.ProductID = 1
AND NOT EXISTS
(SELECT c.ProductID
FROM EAV_Table AS c
JOIN EAV_Table AS d
ON c.Attribute = d.Attribute AND c.value != d.value
WHERE c.ProductID != 1
AND d.ProductID = 1
AND c.ProductID = a.ProductID
)
GROUP BY a.ProductID
それはかなり醜いです。動作しますが、醜いです。
テストデータ
CREATE TABLE eav_table
(
productid INTEGER NOT NULL,
attribute CHAR(5) NOT NULL,
value INTEGER NOT NULL,
PRIMARY KEY(productid, attribute, value)
);
INSERT INTO eav_table VALUES(1, "Prop1", 1);
INSERT INTO eav_table VALUES(1, "Prop2", 2);
INSERT INTO eav_table VALUES(2, "Prop1", 1);
INSERT INTO eav_table VALUES(3, "Prop1", 1);
INSERT INTO eav_table VALUES(3, "Prop2", 3);
INSERT INTO eav_table VALUES(4, "Prop1", 1);
INSERT INTO eav_table VALUES(4, "Prop3", 1);
Q1結果
2 1
3 1
4 1
第2四半期の結果
3 1
第3四半期の結果
2 1
4 1
それらは私が生成したカウントです。より洗練されたレンディションでは、それらが削除されます。
パス 2
それが管理できる場合、より適切な最終クエリは、製品 ID 1 と共通する一致する属性/値のペアが少なくとも 1 つあるすべての製品 ID をリストするテーブルと、一致しないすべての製品 ID をリストするテーブルを結合します。製品 ID 1。
商品 1 と同じ属性を 1 つ以上持つ商品のリスト
最初のクエリは、パス 1 の最初のクエリと同じですが、結果セットのカウントを削除する点が異なります。
SELECT a.ProductID
FROM EAV_Table AS a
JOIN EAV_Table AS b
ON a.Attribute = b.Attribute AND a.value = b.value
WHERE a.ProductID != 1
AND b.ProductID = 1
GROUP BY a.ProductID
通常、選択リスト内の GROUP BY 句または DISTINCT のいずれかが必要です (ただし、サンプル データでは正式には必要ありません)。
商品 1 と一致しない属性がゼロの商品のリスト
COUNT(column)
null 以外の値のみをカウントするという事実を利用して、LEFT OUTER JOIN を使用します。
SELECT c.ProductID
FROM EAV_Table AS c
LEFT JOIN EAV_Table AS d
ON c.Attribute = d.Attribute
AND c.Value != d.Value
AND c.ProductID != 1
AND d.ProductID = 1
GROUP BY c.ProductID
HAVING COUNT(d.Value) == 0;
WHERE 句が ON 句にマージされていることに注意してください。これは実際にはかなり重要です。
結果 — パス 2
上記の 2 つのクエリを、結合して最終結果を生成するサブクエリとして作成します。
SELECT f.ProductID
FROM (SELECT a.ProductID
FROM EAV_Table AS a
JOIN EAV_Table AS b
ON a.Attribute = b.Attribute AND a.value = b.value
WHERE a.ProductID != 1
AND b.ProductID = 1
GROUP BY a.ProductID
) AS e
JOIN (SELECT c.ProductID
FROM EAV_Table AS c
LEFT JOIN EAV_Table AS d
ON c.Attribute = d.Attribute
AND c.Value != d.Value
AND c.ProductID != 1
AND d.ProductID = 1
GROUP BY c.ProductID
HAVING COUNT(D.Value) = 0
) AS f
ON e.ProductID = f.ProductID
これにより、サンプル データの答え 2 と 4 が生成されます。
この演習の一部は、最初に作成した答えに満足しないことを学習していることに注意してください。テーブルに 7 行しかないテスト データ セットではなく、フル サイズのデータ セットでソリューションのベンチマークを行うことをお勧めします。