另一个表中的 SQL SELECT 条件

发布于 2024-09-07 08:25:27 字数 750 浏览 9 评论 0原文

我有 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

我需要使用 properties 表上的条件从 messages 获取数据。 例如:如果我有一个标准,例如返回未读(读=假)高优先级(重要性=高)消息,它应该返回

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

如何使用SELECT子句(MySQL方言)获得这个?

I have 2 related tables:

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

And I need to get from messages using the criteria on the properties table.
Eg: if I have a criteria like return unread (read=false) high prio (importance=high) messages it should return

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

How could I get this with a SELECT clause (MySQL dialect)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

桃扇骨 2024-09-14 08:25:27

在 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< /a>.与使用传统属性相比,其中一个属性属于一列:

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

顺便说一句,@Abe Miessler 和 @Thomas 的答案都比您想要的匹配更多的 mid。它们匹配所有中读=假或重要性=高的中值。您需要将这些属性与等效的 AND 组合起来。

In SQL, any expression in a WHERE clause can only reference one row at a time. So you need some way of getting multiple rows from your properties table onto one row of result. You do this with self-joins:

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';

This shows how awkward it is to use the EAV antipattern. Compare with using conventional attributes, where one attribute belongs in one column:

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

By the way, both answers from @Abe Miessler and @Thomas match more mid's than you want. They match all mid's where read=false OR where importance=high. You need to combine these properties with the equivalent of AND.

居里长安 2024-09-14 08:25:27

我相信下面的查询会起作用。
更新: @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

I believe the query below will work.
UPDATE: @Gratzy is right, this query won't work, take a look at the structure changes I suggested.

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')

The structure of your properties table seems a little off to me though...

Would it be possible to structure the table like this:

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

and use this query:

SELECT m.id as mid, m.subject
FROM message as m
where m.read = false AND m.importance = 3
冧九 2024-09-14 08:25:27

显然,您正在使用 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
                )

Clearly, you are using an EAV (Entity-Attribute-Value) schema. One of the many reasons for avoiding such a structure is that it makes queries more difficult. However, for the example you gave, you could do something like:

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'
                )

A more succinct solution would be:

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
                )
り繁华旳梦境 2024-09-14 08:25:27
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 子句中,并将联接保留在两个表中并且是联接的实际条件的元素。

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'

I prefer to put my filter criteria in the where clause and leave my join's to elements that are in both tables and are the actual criteria for the join.

徒留西风 2024-09-14 08:25:27

另一种方法可能是(未经测试)使用派生表来保存所有消息必须满足的标准,然后使用双 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
               )
       )

Another way might be (untested) to use a derived table to hold the criteria that all messages must meet then use the standard relational division technique of double 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
               )
       )
末が日狂欢 2024-09-14 08:25:27

如果您想保留现有的数据模型,请遵循 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. message(mid) 上的唯一索引和 message(mid) 上的唯一索引属性(pid),我相信你已经有了。
  2. 属性(mid,name)上的唯一索引,以便每个属性只能为一条消息定义一次 - 否则您可能会从查询中获得重复的结果。通过允许两个连接的索引访问,这也将有助于提高查询性能。

If you want to keep your existing data model, then go with Bill Karwin's first suggestion. Run it with this select clause to understand what it's doing:

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';

But if you go this way, there are a few constraints you should put in place to avoid storing and retrieving bad data:

  1. A unique index on message(mid) and a unique index on properties(pid), both of which I'm sure you have already.
  2. A unique index on properties(mid, name) so that each property can only be defined once for a message -- otherwise you may get duplicate results from your query. This will also help your query performance by allowing an index access for both joins.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文