T-SQL - Как написать условное объединение - PullRequest
44 голосов
/ 05 декабря 2009

У меня есть хранимая процедура с рядом параметров. Я хотел бы написать свой запрос так, чтобы он соединялся с определенными таблицами, но только если конкретный параметр имеет значение. Возьмите следующий пример: у меня есть таблица Person. Существует также таблица адресов, которая содержит личные адреса, и таблица групп, которая содержит личные группы. Оба являются отношениями «один ко многим» с таблицей «Персона». Моя хранимая процедура имеет параметр @AddressID и параметр @GroupID.

Запрос всегда просто возвращает поля из таблицы Person. Если ни один из параметров не имеет значения, запрос должен вернуть все записи из таблицы Person. Если указан параметр @AddressID, он должен возвращать только те записи, которые имеют соответствующую запись в таблице адресов, и игнорировать таблицу групп. Если указан параметр @GroupID, он должен возвращать только те записи, которые имеют совпадающую запись в таблице групп, и игнорировать таблицу адресов. Если оба параметра указаны, то в нем должны отображаться только записи с совпадающими записями в обеих таблицах. Имеет смысл?

Есть ли простой способ сделать это, что мне не хватает?

Спасибо, Corey

Ответы [ 9 ]

44 голосов
/ 05 декабря 2009

Если я правильно понимаю, похоже, что ваши условия соединения будут эквивалентны
ON ((@AddressID IS NOT NULL) AND (alias.column = @AddressID)) и аналогично для группового присоединения.

Я иногда использую это условное соединение.

25 голосов
/ 05 декабря 2009

Простые способы на самом деле не являются хорошими решениями. Как бы плохо это не звучало, лучшим решением будет иметь явный IF в коде и отдельные запросы:

IF (condition) 
  SELECT ... FROM Person WHERE ...
ELSE IF (otherCondition)
  SELECT ... FROM Person JOIN ... ON ... WHERE ...
ELSE IF (moreCondition)
  SELECT ... FROM Persons JOIN ... JOIN ... WHERE ...

Причина этого заключается в том, что если вы пытаетесь создать один отдельный запрос, который соответствует всем трем (или более) условиям, то ядро ​​должно создать один единственный план запроса, который работает в все условия. В T-SQL один оператор равен одному плану. Помните, что планы создаются для общего случая, для любого значения переменной, поэтому результатом всегда является очень и очень плохой план.

Хотя это нелогично и кажется ужасным решением для любого программиста, именно так работают базы данных. Причина, по которой это не является проблемой в 99,99% случаев, заключается в том, что после того, как вы выполнили то, что вы спрашиваете, и поняли, что нужно сделать, разработчики быстро пришли в себя и пересмотрели свои требования, чтобы им никогда не приходилось выполнять запросы, которые необязательно объединяются на основе значений переменных времени выполнения;)

7 голосов
/ 05 декабря 2009

Да, это очень просто. У левых включается адрес и группы. Тогда в предложении, где ...

(@group_id is null or g.group_id = @group_id)
and (@address_id is null or a.address_id = @address_id)
3 голосов
/ 11 февраля 2014

Вот как я это сделал для своего дела.


DECLARE
    @ColorParam varchar(500)

SET
    @ColorParam = 'red, green, blue'

declare @Colors table
(
    Color NVARCHAR(50) PRIMARY KEY
)

-- populate @Colors table by parsing the input param, 
-- table can be empty if there is nothing to parse, i.e.: no condition
INSERT @Colors SELECT Value FROM dbo.Splitter(@ColorParam, ',')

SELECT
    m.Col1,
    c.Color
FROM
    MainTable AS m
FULL JOIN -- instead of using CROSS JOIN which won't work if @Colors is empty
    @Colors AS c
ON
    1 = 1 -- the trick
WHERE
    (@ColorParam IS NULL OR c.Color = m.Color)
    
3 голосов
/ 05 декабря 2009

Вы должны быть в состоянии расширить это ...

DECLARE @SQL varchar(max)

    SET @SQL = 'SELECT * FROM PERSON P'

    IF NULLIF(@ADDRESSID,"") IS NULL SET @SQL = @SQL + " INNER JOIN ADDRESSES A ON P.AddressID = A.AddressID"

    EXEC sp_executesql @SQL, N'@ADDRESSID int', @ADDRESSID
0 голосов
/ 12 октября 2016

Соединение влево и пункт, который должен помочь:

SELECT Customers.CustomerName, Customers.Country, Orders.OrderID
    FROM Customers
    LEFT JOIN Orders
    ON Customers.CustomerID=Orders.CustomerID
    WHERE Country= @MyOptionalCountryArg or @MyOptionalCountryArg is null;
0 голосов
/ 25 января 2014

Э-э, наверное, вы все уже решили это.

Насколько я понимаю, вы хотите иметь "динамический" запрос, присоединиться к таблице, если параметр существует, или пропустить объединение, если параметр равен нулю. Секрет в использовании левого внешнего соединения. Как:

SELECT p.*
FROM Parent AS p
LEFT OUTER JOIN Child AS c ON p.Id = c.ParentId
WHERE
        (@ConditionId IS NULL OR c.ConditionId = @ConditionId)

Как это работает?

  • Если параметр фильтра @ConditionId равен нулю, дочерний элемент для внешнего объединения отсутствует, и результат будет иметь все родительские элементы.
  • Если параметр фильтра @ConditionId не равен NULL, то внешнее объединение объединит дочерние элементы с этим родителем, и условие (@ConditionId IS NULL OR c.ConditionId = @ConditionId) исключит родительские элементы, которые не присоединились к дочерним элементам с условием c.ConditionId = @ConditionId.

LEFT OUTER JOIN наверняка имеют проблемы с производительностью, но так как это работает быстро, я не хочу объединять строки для создания запроса.

0 голосов
/ 05 декабря 2009

Соедините три таблицы вместе и используйте что-то подобное в предложении WHERE:

WHERE Addresses.ID = COALESCE(@AddressID, Addresses.ID)
AND  Groups.ID = COALESCE(@GroupID, Groups.ID)
0 голосов
/ 05 декабря 2009

То, что опубликовал Quntin, приятно, но есть некоторые проблемы с производительностью. Верьте или нет то, что быстрее, это проверить каждый параметр и написать SQL Join по делу

Дополнительно:

IF @AddressParameter IS NOT NULL
BEGIN
SELECT blah1, blah2 FROM OneTable INNER JOIN AddressTable WHERE ....
-more code
END
ELSE...
BEGIN
END
...

Еще одна вещь, которую вы можете сделать, это выполнить объединения и выполнить фильтр запросов (предложение where) Вы можете сделать:

WHERE
(Address = @Address OR @Address IS NULL)

Производительность здесь также неясная.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...