Критерии SQL SELECT в другой таблице - PullRequest
4 голосов
/ 01 июля 2010

У меня есть 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 ]

6 голосов
/ 01 июля 2010

В 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 ИЛИ, где важность = высокая. Вам необходимо объединить эти свойства с эквивалентом AND.

5 голосов
/ 01 июля 2010

Я считаю, что приведенный ниже запрос будет работать.
ОБНОВЛЕНИЕ: @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
0 голосов
/ 01 июля 2010

Если вы хотите сохранить существующую модель данных, следуйте первому предложению Билла Карвина.Запустите его с этим предложением 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. Уникальный индекс сообщения (средний) и уникальный индекс свойств (pid), оба из которых, я уверен, у вас уже есть.
  2. Уникальный индекс свойств (середина, имя)так что каждое свойство может быть определено только один раз для сообщения - в противном случае вы можете получить дублированные результаты вашего запроса.Это также поможет повысить производительность запросов, предоставив доступ к индексу для обоих объединений.
0 голосов
/ 01 июля 2010

Другой способ (не проверенный) - использовать производную таблицу для хранения критериев, которым должны соответствовать все сообщения, затем использовать стандартную технику реляционного деления: двойное 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
               )
       )
0 голосов
/ 01 июля 2010
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 и оставлять мои объединения для элементов, которые находятся в обеих таблицах и являются фактическими критериями для объединения.

0 голосов
/ 01 июля 2010

Очевидно, что вы используете схему EAV (Entity-Attribute-Value). Одна из многих причин избегать такой структуры заключается в том, что она усложняет запросы. Однако для приведенного вами примера вы можете сделать что-то вроде:

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
                )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...