我有一个“产品”表和一个存储产品某些属性的表:
zd_products
----------
|ID|title|
----------
| 1| Test|
| 2| Prod|
| 3| Colr|
zd_product_attached_attributes
------------------
|attrid|pid|value|
------------------
|1 | 1 | A |
|2 | 1 | 10 |
|3 | 1 | AB |
|1 | 2 | B |
|2 | 2 | 22 |
|3 | 2 | BB |
|1 | 3 | A |
|2 | 3 | 10 |
|3 | 3 | CC |
我只想在 zd_products 中搜索具有某些属性值的产品,以便考试 获取属性1为A,属性3为AB时的商品 获取属性2为10、属性3为CC时的商品 等等
我如何使用连接来做到这一点?
最佳答案
哦,EAV 模型的乐趣!
一种方法是对每个属性值使用单独的 JOIN 操作。例如:
SELECT p.id
, p.title
FROM zd_products p
JOIN zd_product_attached_attributes a1
ON a1.pid = p.id
AND a1.attrid = 1
AND a1.value = 'A'
JOIN zd_product_attached_attributes a3
ON a3.pid = p.id
AND a3.attrid = 3
AND a3.value = 'AB'
有了适当的索引,这可能是最有效的方法。这不是唯一会返回指定结果的查询,但这确实使用了 JOIN 操作。
另一种不太直观的方法
如果id
在 zd_products 表中是唯一的,我们保证 (attrid,pid,value)
元组在 zd_product_attached_attributes
中是唯一的表,然后是这个:
SELECT p.id
, p.title
FROM zd_products p
JOIN zd_product_attached_attributes a
ON a.pid = p.id
AND ( (a.attrid = 1 AND a.value = 'A')
OR (a.attrid = 3 AND a.value = 'AB')
)
GROUP
BY p.id
, p.title
HAVING COUNT(1) > 1
将返回等效结果。后一种查询的形式特别适合匹配三个条件中的两个条件,我们不需要匹配所有属性,只需要匹配其中的一些属性。例如,查找与以下任意两项匹配的产品:
- color = '黄色'
- size = '更大'
- 特别='着火'
当然还有其他不使用 JOIN 的方法。
跟进
问: 如果我想要相同但使用 OR
运算符(operator)?我的意思是仅当属性 1 是 A 或属性 2 是 AB 时才获取,否则不要选择记录。
答:像我的回答(上面)中的第二种形式的查询更有利于 OR
条件。
如果您想要 XOR(异或),其中一个属性具有匹配值而另一个没有,只需更改 HAVING COUNT(1) > 1
至 HAVING COUNT(1) = 1
.只有在属性表中找到一个“匹配”行的产品行才会被返回。要恰好匹配 2 个(几个中的一个),HAVING COUNT(1) = 2
等
像我的回答中的第一个这样的查询可以修改为使用外部连接来查找匹配项,然后在 WHERE 子句中进行条件测试以确定是否找到匹配项。
SELECT p.id
, p.title
FROM zd_products p
LEFT
JOIN zd_product_attached_attributes a1
ON a1.pid = p.id
AND a1.attrid = 1
AND a1.value = 'A'
LEFT
JOIN zd_product_attached_attributes a3
ON a3.pid = p.id
AND a3.attrid = 3
AND a3.value = 'AB'
WHERE a1.pid IS NOT NULL
OR a3.pid IS NOT NULL
我刚刚添加了 LEFT
关键字,指定外连接;来自产品的行将与来自 a1 和 a3 的匹配行一起返回,以及来自没有在 a1 或 a3 中找到任何匹配行的产品的行。
WHERE
子句测试 a1 和 a3 中的列以查看是否返回了匹配的行。如果在 a1 中找到匹配行,我们可以保证 pid
a1 中的列将是非 NULL。仅当未找到匹配行时,该列才会作为 NULL 返回。
如果我们用 AND 替换 OR,我们将否定两个连接的“外部性”,使其基本上等同于上面的第一个查询。
要获得 XOR 类型的操作(异或),我们找到一个匹配的属性但没有找到另一个,我们可以将 WHERE 子句更改为:
WHERE (a1.pid IS NOT NULL AND a3.pid IS NULL)
OR (a3.pid IS NOT NULL AND a1.pid IS NULL)
关于Mysql搜索多个连接结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22823766/