另一个表中的SQL SELECT条件

时间:2010-06-30 21:06:00

标签: sql mysql entity-attribute-value

我有2个相关的表格:

messages
--------

mid subject
--- -----------------
1   Hello world
2   Bye world
3   The third message
4   Last one


properties
----------

pid mid name             value
--- --- ---------------- ----------- 
1   1   read             false
2   1   importance       high
3   2   read             false
4   2   importance       low
5   3   read             true
6   3   importance       low
7   4   read             false
8   4   importance       high

我需要使用messages表上的条件从properties获取。 例如:如果我有return unread (read=false) high prio (importance=high) messages之类的标准,则应返回

mid subject
--- -----------------
1   Hello world
4   Last one

我怎么能用SELECT子句(MySQL方言)得到它?

6 个答案:

答案 0 :(得分:6)

在SQL中,WHERE子句中的任何表达式一次只能引用一行。因此,您需要某种方法将属性表中的多行添加到一行结果中。你用自联接做到这一点:

SELECT ...
FROM messages AS m
JOIN properties AS pRead 
    ON m.mid = pRead.mid AND pRead.name = 'read'
JOIN properties AS pImportance 
    ON m.mid = pImportance.mid AND pImportance.name = 'importance'
WHERE pRead.value = 'false' AND pImportance.value = 'high';

这表明使用EAV antipattern有多么尴尬。与使用常规属性进行比较,其中一个属性属于一列:

SELECT ...
FROM messages AS m
WHERE m.read = 'false' AND m.importance = 'high';

顺便说一句,@ Abe Miessler和@Thomas的答案都比你想要的更多。它们匹配所有中间值,其中read = false或者important = high。您需要将这些属性与AND的等效项组合在一起。

答案 1 :(得分:5)

我相信下面的查询会有效。
更新: @Gratzy是对的,这个查询不起作用,看看我建议的结构变化。

SELECT DISTINCT m.id as mid, m.subject
FROM message as m
INNER JOIN properties as p
ON m.mid = p.mid
where (p.name = 'read' and p.value = 'false') or (p.name = 'importance' AND p.value = 'high')

你的属性表的结构虽然对我来说有点不对......

是否可以像这样构建表:

messages
--------

mid subject           Read      Importance
--- ----------------- --------- ------------
1   Hello world       false     3
2   Bye world         false     1
3   The third message true      1
4   Last one          false     3

importance
----------

iid importanceName
--- --------------
1   low
2   medium
3   high

并使用此查询:

SELECT m.id as mid, m.subject
FROM message as m
where m.read = false AND m.importance = 3

答案 2 :(得分:0)

显然,您正在使用EAV(实体 - 属性 - 值)架构。避免这种结构的众多原因之一是它使查询更加困难。但是,对于您给出的示例,您可以执行以下操作:

Select ...
From messages As M
Where Exists    (
                Select 1
                From Properties As P1
                Where P1.mid = M.mid
                    And P1.name = 'unread' And P1.value = 'false'
                )
    And Exists  (
                Select 1
                From Properties As P2
                Where P2.mid = M.mid
                    And P2.name = 'importance' And P2.value = 'high'
                )

更简洁的解决方案是:

Select ...
From messages As M
Where Exists    (
                Select 1
                From Properties As P1
                Where P1.mid = M.mid
                    And ((P1.name = 'unread' And P1.value = 'false')
                            Or (P1.name = 'importance' And P1.value = 'high'))
                Having Count(*) = 2
                )

答案 3 :(得分:0)

Select m.mid, m.subject
from properties p 
inner join properties p1 on p.mid = p1.mid
inner join messages m on p.mid = m.mid
where
p.name = 'read' 
and p.value = 'false'
and p1.name = 'importance'
and p2.value = 'high'

我更喜欢将我的过滤条件放在where子句中,并将我的连接保留给两个表中的元素,并且是连接的实际条件。

答案 4 :(得分:0)

另一种方法可能是(未经测试)使用派生表来保存所有消息必须满足的条件,然后使用双NOT EXISTS的标准关系除法技术

SELECT mid,
       subject
FROM   messages m
WHERE  NOT EXISTS
       ( SELECT *
       FROM    ( SELECT 'read' AS name,
                       'false' AS value

               UNION ALL

               SELECT 'importance' AS name,
                      'high'       AS value
               )
               c
       WHERE   NOT EXISTS
               (SELECT *
               FROM    properties P
               WHERE   p.mid  = m.mid
               AND     p.name =c.name
               AND     p.value=c.value
               )
       )

答案 5 :(得分:0)

如果您想保留现有的数据模型,请参阅Bill Karwin的第一个建议。使用此select子句运行它以了解它正在做什么:

select m.*, r.value as read, i.value as importance
from message m
join properties r
    on r.mid = m.mid and r.name = 'read'
join properties i
    on i.mid = m.mid and i.name = 'importance'
where r.value = 'false' and i.value = 'high';

但如果你这样做,你应该采取一些限制措施来避免存储和检索不良数据:

  1. 关于消息(mid)的唯一索引和属性(pid)上的唯一索引,我相信这两者都已经存在。
  2. 属性的唯一索引(mid,name),以便每个属性只能为消息定义一次 - 否则您可能会从查询中获得重复的结果。这也可以通过允许两个连接的索引访问来帮助您提高查询性能。