Mysql搜索多个连接结果

时间:2014-04-02 21:47:44

标签: mysql search join

我有一个表“产品”和一个表,其中存储了产品的一些属性:

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时获取产品 等

如何使用连接执行此操作?

3 个答案:

答案 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

将返回相同的结果。后一个查询的形式特别适合于匹配三个中的两个条件,其中我们不需要匹配所有属性,而只需要其中一些属性。例如,找到与以下任意两个匹配的产品:

  • color ='黄色'
  • size ='更大'
  • special ='着火'

当然还有其他方法不能使用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;语句)