1

我敢打赌这是一个简单的问题,但我的 MySQL 印章有点生疏了。需要一点帮助来为以下使用多条件连接的场景生成正确的查询。我有一个属性表(想想标签)和另一个表,其中有一个记录列表。当我想匹配在查询中定义了多个属性的页面时遇到问题。

+--------------------------------------+
| ATTRIBUTES                           |
+-----------+------------+-------------+
| name      | value      | page_id     |
+-----------+------------+-------------+
| type        food         2           |  
| fruit       apple        2           |  
| color       green        2           |
| type        frog         3           |  
| color       green        3           |
+--------------------------------------+


+--------------------------------------+
| PAGES                                |
+--------------+-----------------------+
| page_id      | title                 |
+--------------+-----------------------+
| 2              Granny Smith          |  
| 3              Kermit the Frog       |
+--------------------------------------+

用例

Query: type+food
Results: Granny Smith

Query: color+green
Results: Granny Smith, Kermit the Frog

Query: type+food and color+green
Results: Granny Smith

这是我正在玩的查询,但显然不起作用:

SELECT page.page_id, page.title 
FROM page_attributes 
LEFT JOIN page ON (page_attributes.page_id = page.page_id) 
WHERE (page_attributes.name = 'color' 
AND page_attributes.value = 'green') 
AND (page_attributes.name = 'type' 
AND page_attributes.value = 'food')
4

3 回答 3

2

做到这一点的一种方法,即使可能有更有效的方法,也是制作一个具有多个INNER JOINs 的查询,每个属性一个。使用INNER JOIN, 与 a 相反,LEFT JOIN所有条件必须为真,主行才能匹配。

SELECT page.page_id, page.title 
  FROM page
INNER JOIN page_attributes a1 ON (a1.page_id = page.page_id)
INNER JOIN page_attributes a2 ON (a2.page_id = page.page_id)
 WHERE (a1.name = 'color' AND a1.value = 'green') 
   AND (a2.name = 'type'  AND a2.value = 'food')
LIMIT 10
于 2013-05-14T17:14:43.227 回答
0

与 Nitro 相同,但使用 ON 子句保留属性的标准。此外,请确保您在 ( page_id, name, value ) 上的属性表上有一个索引,以便优化连接。

SELECT 
      page.page_id, 
      page.title 
   FROM 
      page

         INNER JOIN page_attributes a1 
           ON page.page_id = a1.page_id
           AND a1.name = 'color'
           AND a1.value = 'green'

         INNER JOIN page_attributes a2
           ON page.page_id = a2.page_id
           AND a2.name = 'type'
           AND a2.value = 'food'

   LIMIT 10

如您所见,您想添加另一个“标准”,只需复制/粘贴整个联接。它要么找到匹配项并允许包含,要么不找到并忽略页面记录。

于 2013-05-14T17:23:18.050 回答
0

问题是您试图同时将所有条件应用于连接的不同行。以下是连接关系(在任何SELECTWHERE过滤之前)可能的样子:

Joined relation:
+--------------+-----------------------+-------------+-------------+
| page_id      | title                 | name        | value       |
+--------------+-----------------------+-------------+-------------+
| 2              Granny Smith            type          food        |
| 2              Granny Smith            color         green       |
| 2              Granny Smith            fruit         apple       |
| 3              Kermit the Frog         color         green       |
| 3              Kermit the Frog         type          frog        |
+--------------+-----------------------+-------------+-------------+

WHERE 子句将应用于此中间结果的每一行。因此,查询中的子句:

WHERE (page_attributes.name = 'color' 
AND page_attributes.value = 'green') 
AND (page_attributes.name = 'type' 
AND page_attributes.value = 'food')

对于该连接关系的每一行,都将为 false。

简单地将 s 更改为ANDs 是OR行不通的,因为这只会找到具有任何标签值的页面,而不是所有标签值,这是您真正希望查询执行的操作。

不幸的是,对于这种表结构,没有通用的、标准化的方法可以对任意数量的要查询的标签执行此操作。page_attributes如果您事先知道将查询多少个标签,则其他答案中有关使用表的多个连接的建议将起作用,并且您可以通过多个UNIONed 查询实现类似的效果。但同样,这些只有在您修复要查询的标签数量时才有效。

最简单的通用解决方案是避免尝试在 SQL 中完成所有操作。执行连接和任何WHERE不依赖于标记值的条件,然后让应用程序代码迭代结果并强制执行标记查询逻辑。

于 2013-05-14T17:32:53.670 回答