我有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方言)得到它?
答案 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';
但如果你这样做,你应该采取一些限制措施来避免存储和检索不良数据: