遇到此类问题时,我使用 TDQD — 测试驱动的查询设计。
请注意,如果你给你的桌子起个名字,它会对每个人都有帮助!
通过 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 不匹配的产品列表
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
我们需要第一个查询中未在第二个查询中列出的所有产品。这可以通过 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);
第一季度结果
2 1
3 1
4 1
第二季度结果
3 1
第三季度结果
2 1
4 1
这些是我生成的计数;更优美的演绎将删除它们。
通过 2
如果可以管理,一个更好的最终查询将连接一个表,该表列出了与产品 ID 1 具有至少一个匹配属性/值对的所有产品 ID产品编号 1。
一个或多个属性与产品 1 相同的产品列表
第一个查询与 Pass 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)
我们将利用仅计算非空值的事实,并使用 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
我们将上面的两个查询构建为连接以生成最终结果的子查询:
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 行的测试数据集上进行基准测试。