T-SQL - 如何编写条件连接

时间:2009-12-04 22:02:11

标签: tsql join

我有一个包含许多参数的存储过程。我想编写我的查询,以便它与某些表连接,但仅当特定参数具有值时。举个例子:我有一个Person表。还有一个Address表,其中包含Person Addresses和一个包含Person Groups的Groups表。两者都是与Person表的一对多关系。我的存储过程有一个@AddressID参数和一个@GroupID参数。

查询始终只返回Person表中的字段。如果两个参数都没有值,则查询应返回Person表中的所有记录。如果提供了@AddressID参数,则它应仅返回在Address表中具有匹配记录的记录,并忽略Groups表。如果提供了@GroupID参数,则它应仅返回在Groups表中具有匹配记录的记录,并忽略Addresses表。如果提供了两个参数,那么它应该只显示两个表中具有匹配记录的记录。有意义吗?

有一种简单的方法可以解决这个问题吗?

谢谢, 科里

9 个答案:

答案 0 :(得分:44)

如果我理解正确,这听起来像你的加入条件相当于 ON ((@AddressID IS NOT NULL) AND (alias.column = @AddressID))同样适用于小组加入。

我有时会使用这种条件连接。

答案 1 :(得分:25)

简单的方法实际上不是好的解决方案。听起来很糟糕,最好的解决方案是在代码和单独的查询中使用显式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%的时间这不是问题的原因是,在尝试了你所要求的内容并看到它必须完成之后,开发人员很快就会明白并修改他们的需求,这样他们就不必运行可选择加入的查询基于运行时变量值;)

答案 2 :(得分:7)

是的,这很简单。在地址和组上留下联接。然后在where子句......

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

答案 3 :(得分:3)

你应该可以扩展这个......

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

答案 4 :(得分:3)

这就是我为我的案子所做的。


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 :(得分:0)

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)

这里的表现也很阴暗。

答案 6 :(得分:0)

将三个表连接在一起,并在WHERE子句中使用类似的东西:

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

答案 7 :(得分:0)

呃,到目前为止你们所有人都已经解决了这个问题。

据我了解你,你想要'动态'查询,如果参数存在则连接表,或者如果参数为空则省略连接。 秘密在于使用左外连接。像:

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不为null,则外连接将使用此父连接加入Child,并且条件(@ConditionId IS NULL OR c.ConditionId = @ConditionId)将抛出未加入具有条件c.ConditionId = @ConditionId的Child的父组件。

LEFT OUTER JOIN肯定有性能问题,但尽管这很快,我不想连接字符串来创建查询。

答案 8 :(得分:0)

左连接和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;