Проблема с левым соединением в SQL-запросе - PullRequest
0 голосов
/ 03 мая 2018

У меня три таблицы Post, PostTagMap и Tag. PostTagMap - это таблица соединений для отображения одного сообщения с несколькими тегами. Я пытаюсь написать запрос, чтобы вытащить все сообщения, принадлежащие тегу name .

Почтовый стол

Id    Name

1     Post #1

2     Post #2

Таблица тегов

Id   Name

1     JS

2     CSS

Таблица PostTagMap

Id   PostId   TagId

1       1       1
2       1       2

(отредактированные данные PostTagMap для соответствия рассматриваемой проблеме)


SELECT P.Id FROM Post P
LEFT JOIN PostTagMap PTM ON P.Id = PTM.PostId
INNER JOIN Tag T ON PTM.TagId = T.Id
WHERE T.Name = ISNULL(@Tag, T.Name)

Когда @Tag равно NULL Я получаю только детали идентификатора записи 1, а не 2. Что не так с приведенным выше запросом?

Пробовал и ниже,

SELECT P.Id FROM Post P
LEFT JOIN PostTagMap PTM ON P.Id = PTM.PostId
LEFT JOIN Tag T ON PTM.TagId = T.Id
WHERE T.Name = ISNULL(@Tag, T.Name)

Выше также не возвращаются детали сообщения 2.

SELECT P.Id FROM Post P
LEFT JOIN PostTagMap PTM ON P.Id = PTM.PostId
LEFT JOIN Tag T ON PTM.TagId = T.Id AND T.Name = ISNULL(@Tag, T.Name)

Возвращая все, даже передавая @Tag как "JS".

Ответы [ 4 ]

0 голосов
/ 03 мая 2018

ваша проблема

WHERE T.Name = ISNULL(@Tag, T.Name)

потому что T.Name = T.Name ЛОЖНО, когда T.Name IS NULL

измените пункт where в

WHERE (T.Name = @Tag) or (@Tag IS NULL)

также используйте оба параметра LEFT JOIN, или внутренний будет отфильтровывать строки без записи в соединительной таблице

0 голосов
/ 03 мая 2018

Я бегу:

SELECT P.Id FROM Post P
LEFT JOIN PostTagMap PTM ON P.Id = PTM.PostId
LEFT JOIN Tag T ON PTM.TagId = T.Id
WHERE T.Name = COALESCE(@Tag, T.Name)

И когда @Tag равен NULL, он возвращает мне все. Я не знаю, если это то, что вы ожидаете.

Вот мой Скрипка

0 голосов
/ 03 мая 2018

Поскольку вы используете LEFT JOIN со строками, которые зависят от условия @Tag, вы в основном никогда не фильтруете Post записей, поэтому вы всегда получаете все их .

Это правда, что вы выбираете дополнительные данные из таблиц слева, если есть совпадающие строки, но это не влияет на то, какие записи Post возвращаются.

Что нужно сделать вместо этого:
Если @Tag равен нулю - не фильтровать .
В противном случае - примените фильтр и сделайте это, используя inner join :

DECLARE @Tag NVARCHAR(50) = 'Js'   -- or NULL

SELECT P.Id FROM Post P
WHERE @Tag IS NULL
OR EXISTS (
  SELECT 1 FROM PostTagMap M
  JOIN Tag T ON M.TagId = T.Id
  WHERE M.PostId = P.Id
  AND   T.Name = @Tag
)
0 голосов
/ 03 мая 2018

Я перепробовал все ваши запросы, но все работает нормально, попробуйте это

SQLFiddle

каждый запрос работает так, как должен, Может быть, проблема не в запросе

SELECT P.Id FROM Post P
LEFT JOIN PostTagMap PTM ON P.Id = PTM.PostId
INNER JOIN Tag T ON PTM.TagId = T.Id
WHERE T.Name = ISNULL('Css', T.Name)

вернуть только пост 1, как ожидалось, не все

Edit:

Я понимаю вашу проблему, теперь попробуйте мой запрос

DECLARE @Tag NVARCHAR(50)
set @Tag = NULL

SELECT P.Id FROM Post P
LEFT JOIN PostTagMap PTM ON P.Id = PTM.PostId
LEFT JOIN Tag T ON PTM.TagId = T.Id
WHERE @Tag IS NULL OR T.Name = @Tag
GROUP BY P.Id

Выберите все Post, затем добавьте WHERE @Tag IS NULL на случай, если ваш @Tag будет нулевым, а другой для T.Name = @Tag, надеюсь, он может помочь

...