Ответ 1
Если я правильно понимаю, это звучит так, как будто ваши условия соединения будут эквивалентны
ON ((@AddressID IS NOT NULL) AND (alias.column = @AddressID))
, а также для группового соединения.
Я использую это условное соединение в разы.
У меня есть хранимая процедура с несколькими параметрами. Я хотел бы написать свой запрос так, чтобы он соединялся с определенными таблицами, но только если конкретный параметр имеет значение. Возьмем следующий пример: у меня есть таблица Person. Существует также таблица адресов, в которой содержатся адреса лиц и таблица групп, в которых содержатся группы лиц. Оба являются отношениями друг к другу с таблицей Person. Моя хранимая процедура имеет параметр @AddressID и параметр @GroupID.
Запрос всегда просто возвращает поля из таблицы Person. Если ни один из параметров не имеет значения, запрос должен возвращать все записи из таблицы Person. Если задан параметр @AddressID, он должен возвращать только записи, имеющие соответствующую запись в таблице Address, и игнорировать таблицу Groups. Если задан параметр @GroupID, он должен возвращать только записи с соответствующей записью в таблице "Группы" и игнорировать таблицу "Адреса". Если оба параметра поставлены, то они должны показывать только записи, имеющие соответствующую запись в обеих таблицах. Есть смысл?
Есть ли простой способ сделать это, что мне не хватает?
Спасибо, Corey
Если я правильно понимаю, это звучит так, как будто ваши условия соединения будут эквивалентны
ON ((@AddressID IS NOT NULL) AND (alias.column = @AddressID))
, а также для группового соединения.
Я использую это условное соединение в разы.
Простые способы - это не очень хорошие решения. Столь же плохо, как кажется, лучшим решением является наличие явного 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% времени, состоит в том, что после того, как вы попробуете то, что вы спросите, и посмотрите, что нужно сделать, разработчики быстро опомнились и пересмотрели свои требования, чтобы им никогда не приходилось запускать запросы, которые необязательно присоединяются основанные на значениях переменных времени выполнения;)
Да, это очень просто. Оставайтесь слева от адреса и групп. Затем в предложении where...
(@group_id is null or g.group_id = @group_id)
and (@address_id is null or a.address_id = @address_id)
Вот как я это сделал для своего дела.
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)
Вы должны быть в состоянии расширить это...
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
Что такое 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)
Производительность здесь также теневая.
Соедините три таблицы вместе и используйте что-то вроде этого в своем предложении WHERE:
WHERE Addresses.ID = COALESCE(@AddressID, Addresses.ID)
AND Groups.ID = COALESCE(@GroupID, Groups.ID)
Скорее всего, вы все это решили.
Как я понимаю вас, вы хотите иметь "динамический" запрос, присоединиться к таблице, если параметр существует, или опустить 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 IS NULL OR c.ConditionId = @ConditionId)
выкинет Parent, который не присоединился к Child с условием c.ConditionId = @ConditionId
.У LEFT OUTER JOIN есть проблема с производительностью, но насколько это работает быстро, я не хочу конкатенировать строку для создания запроса.
Левое соединение и предложение 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;