我有一个表“产品”和一个表,其中存储了产品的一些属性:
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时获取产品 等
如何使用连接执行此操作?
答案 0 :(得分:3)
哦,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
将返回相同的结果。后一个查询的形式特别适合于匹配三个中的两个条件,其中我们不需要匹配所有属性,而只需要其中一些属性。例如,找到与以下任意两个匹配的产品:
当然还有其他方法不能使用JOIN。
<强>后续强>
问:如果我想使用OR
运算符,我想要相同的话?我的意思是,只有当属性1为A或属性2为AB时才能获取,否则不要选择记录。
答:我的答案(上图)中的第二个表单查询更有利于OR
条件。
如果您想要XOR(异或),其中一个属性具有匹配值但另一个属性不匹配,只需将HAVING COUNT(1) > 1
更改为HAVING COUNT(1) = 1
即可。只有找到一个&#34;匹配&#34;的产品中的行将返回属性表中的行。准确匹配2(几个),HAVING COUNT(1) = 2
等
我的答案中的第一个查询可以修改为使用OUTER连接,查找匹配项,然后在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中找到匹配的行,我们保证a1中的pid
列将为非NULL。仅当找不到匹配的行时,该列才会返回NULL。
如果我们用AND替换OR,我们就会否定&#34;外部性&#34;两个连接,使它基本上等同于上面的第一个查询。
要获得XOR类型操作(异或),我们找到一个匹配属性而不是另一个,我们可以将WHERE子句更改为:
WHERE (a1.pid IS NOT NULL AND a3.pid IS NULL)
OR (a3.pid IS NOT NULL AND a1.pid IS NULL)
答案 1 :(得分:1)
您可以使用 pivot 执行此类查询。据我所知,MySQL没有本机的内置数据透视图,但您可以通过使用以下内容转置zd_product_attached_attributes
表的行和列来实现此目的:
SELECT pid,
MAX(CASE WHEN attrid = 1 THEN value END) `attrid_1`,
MAX(CASE WHEN attrid = 2 THEN value END) `attrid_2`,
MAX(CASE WHEN attrid = 3 THEN value END) `attrid_3`
FROM zd_product_attached_attributes
GROUP BY pid
这将如图所示转动您的表格:
+----+---------+-------+ +----+----------+----------+----------+
| attrid | pid | value | | pid| attrid_1 | attrid_2 | attrid_3 |
+----+---+-------------+ +----+----------+----------+----------+
| 1 | 1 | A | | 1 | A | 10 | AB |
| 2 | 1 | 10 | => | 2 | B | 22 | BB |
| 3 | 1 | AB | | 3 | A | 10 | CC |
| 1 | 2 | B | +----+----------+----------+----------+
| 2 | 2 | 22 |
| 3 | 2 | BB |
| 1 | 3 | A |
| 2 | 3 | 10 |
| 3 | 3 | CC |
+--------+---------+---+
因此,您可以使用以下方式选择产品ID和标题:
SELECT id, title FROM zd_products
LEFT JOIN
(
SELECT pid,
MAX(CASE WHEN attrid = 1 THEN value END) `attrid_1`,
MAX(CASE WHEN attrid = 2 THEN value END) `attrid_2`,
MAX(CASE WHEN attrid = 3 THEN value END) `attrid_3`
FROM zd_product_attached_attributes
GROUP BY pid
) AS attrib_search
ON id = pid
WHERE ( attrib_1 = 'A' AND attrib_3 = 'AB' )
OR ( attrib_2 = 10 AND attrib_3 = 'CC' )
注意:当您在(pid,attrid)上保证唯一性时,您可以使用此类型的查询
(谢谢@ spencer7593)
答案 2 :(得分:0)
我还没有对此进行测试,但我认为它应该有效:
select title
from zd_products p
join zd_product_attached_attributes a ON a.pid = p.id
where ( attrid = 1 and value = 'A' )
or ( attrid = 3 and value = 'AB' );
如果你想进行更多的搜索&#34;你可以添加更多类似于最后一行的行(即。或&#34;或&#34;语句)