удаление строк из SELECT на основе столбцов в другой таблице - PullRequest
5 голосов
/ 12 января 2011

Я в значительной степени ищу способ отфильтровать строки из SELECT одной таблицы на основе определенных значений в строках другой таблицы.

Я экспериментирую с примером структуры ниже.У меня есть таблица содержимого записей в блоге (одна строка для каждого сообщения в блоге) и другая таблица метаданных о сообщениях (одна строка на пару ключ-значение; каждая строка содержит столбец, связывающий его с сообщением в блоге;Сообщение блога).Я хочу получить строку из posts только в том случае, если в metadata нет строк, где metadata.pid=posts.pid AND metadata.k='optout'.То есть, для приведенной ниже структуры примера я просто хочу получить строку posts.id=1.

(основываясь на том, что я пробовал) JOIN s не заканчивают тем, что удаляют сообщения, которые имеютнекоторые метаданные, где metadata.k='optout', потому что другая строка метаданных для этого pid означает, что оно превращает их в результаты.

mysql> select * from posts;
+-----+-------+--------------+
| pid | title | content      |
+-----+-------+--------------+
|   1 | Foo   | Some content |
|   2 | Bar   | More content |
|   3 | Baz   | Something    |
+-----+-------+--------------+
3 rows in set (0.00 sec)

mysql> select * from metadata;
+------+-----+--------+-----------+
| mdid | pid | k      | v         |
+------+-----+--------+-----------+
|    1 |   1 | date   | yesterday |
|    2 |   1 | thumb  | img.jpg   |
|    3 |   2 | date   | today     |
|    4 |   2 | optout | true      |
|    5 |   3 | date   | tomorrow  |
|    6 |   3 | optout | true      |
+------+-----+--------+-----------+
6 rows in set (0.00 sec)

Подзапрос может дать мне обратное тому, что я хочу:

mysql> select posts.* from posts where pid = any (select pid from metadata where k = 'optout');
+-----+-------+--------------+
| pid | title | content      |
+-----+-------+--------------+
|   2 | Bar   | More content |
|   3 | Baz   | Something    |
+-----+-------+--------------+
2 rows in set (0.00 sec)

... но использование pid != any (...) дает мне все 3 строки в сообщениях, потому что у каждого pid есть строка метаданных, где k!='optout'.

Ответы [ 2 ]

8 голосов
/ 12 января 2011

Звучит так, будто вы хотите сделать LEFT JOIN, а затем проверить результаты, в которых значение объединенной таблицы равно NULL, указывая, что такой объединенной записи не существует.

Например:

SELECT * FROM posts 
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout')
WHERE metadata.mdid IS NULL;

Это выберет любую строку из таблицы posts, для которой не существует соответствующей строки metadata со значением k = 'optout'.

edit: Стоит отметить, что это ключевое свойство левого соединения и не будет работать с обычным соединением; левое соединение всегда будет возвращать значения из первой таблицы, даже если в объединенной таблице (таблицах) не найдено соответствующих значений, что позволяет выполнять выборки на основе отсутствия этих строк.

edit 2: Давайте проясним, что здесь происходит в отношении LEFT JOIN против JOIN (для ясности я называю INNER JOIN, но взаимозаменяемы в MySQL).

Предположим, вы выполняете любой из этих двух запросов:

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
INNER JOIN metadata ON posts.pid = metadata.pid;

или

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
LEFT JOIN metadata ON posts.pid = metadata.pid;

Оба запроса выдают следующий набор результатов:

+-----+-------+--------------+------+-------+-----------+
| pid | title | content      | mdid | k     | v         |
+-----+-------+--------------+------+-------+-----------+
|   1 | Foo   | Some content |    1 | date  | yesterday |
|   1 | Foo   | Some content |    2 | thumb | img.jpg   |
+-----+-------+--------------+------+-------+-----------+

Теперь давайте предположим, что мы изменили запрос, чтобы добавить дополнительные критерии для "отказа", которые были упомянуты. Во-первых, INNER JOIN:

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
INNER JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout");

Как и ожидалось, это не даст результатов:

Empty set (0.00 sec)

Теперь, изменив это на LEFT JOIN:

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout");

Это создает результат:

+-----+-------+--------------+------+------+------+
| pid | title | content      | mdid | k    | v    |
+-----+-------+--------------+------+------+------+
|   1 | Foo   | Some content | NULL | NULL | NULL |
+-----+-------+--------------+------+------+------+

Разница между INNER JOIN и LEFT JOIN заключается в том, что INNER JOIN вернет результат, только если строки из ОБА соединенных таблиц совпадают. В LEFT JOIN соответствующие строки из первой таблицы будут ВСЕГДА возвращаться независимо от того, найдено ли что-либо для соединения. Во многих случаях не имеет значения, какой из них вы используете, но важно выбрать правильный, чтобы не получить неожиданные результаты в будущем.

Так что в этом случае предложенный запрос:

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout')
WHERE metadata.mdid IS NULL;

Вернет тот же набор результатов, что и выше:

+-----+-------+--------------+------+------+------+
| pid | title | content      | mdid | k    | v    |
+-----+-------+--------------+------+------+------+
|   1 | Foo   | Some content | NULL | NULL | NULL |
+-----+-------+--------------+------+------+------+

Надеюсь, это прояснит ситуацию! Объединение - это отличная вещь для изучения, так как у нее есть полное понимание того, когда использовать, и это очень хорошая вещь.

3 голосов
/ 12 января 2011

Вы можете попробовать что-то вроде

select  p.* 
from    posts p
where   NOT EXISTS (
                        select  pid 
                        from    metadata 
                        where   k = 'optout' 
                        and     pid = p.pid
                    )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...