Подтвердить что ты не робот

T-SQL - Как написать условное соединение

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

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

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

Спасибо, Corey

4b9b3361

Ответ 1

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

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

Ответ 2

Простые способы - это не очень хорошие решения. Столь же плохо, как кажется, лучшим решением является наличие явного 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% времени, состоит в том, что после того, как вы попробуете то, что вы спросите, и посмотрите, что нужно сделать, разработчики быстро опомнились и пересмотрели свои требования, чтобы им никогда не приходилось запускать запросы, которые необязательно присоединяются основанные на значениях переменных времени выполнения;)

Ответ 3

Да, это очень просто. Оставайтесь слева от адреса и групп. Затем в предложении where...

(@group_id is null or g.group_id = @group_id)
and (@address_id is null or a.address_id = @address_id)

Ответ 4

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


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)
    

Ответ 5

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

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

Ответ 6

Что такое 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)

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

Ответ 7

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

WHERE Addresses.ID = COALESCE(@AddressID, Addresses.ID)
AND  Groups.ID = COALESCE(@GroupID, Groups.ID)

Ответ 8

Скорее всего, вы все это решили.

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

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 имеет значение NULL, тогда нет никакого дочернего элемента для внешнего соединения, и результат будет иметь все родительские элементы.
  • Если параметр фильтра @ConditionId не является нулевым, то внешнее соединение будет присоединяться к Child с этим родителем, а условие (@ConditionId IS NULL OR c.ConditionId = @ConditionId) выкинет Parent, который не присоединился к Child с условием c.ConditionId = @ConditionId.

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

Ответ 9

Левое соединение и предложение where должно выполнить трюк:

SELECT Customers.CustomerName, Customers.Country, Orders.OrderID
    FROM Customers
    LEFT JOIN Orders
    ON Customers.CustomerID=Orders.CustomerID
    WHERE Country= @MyOptionalCountryArg or @MyOptionalCountryArg is null;