web-dev-qa-db-ja.com

T-SQL-条件結合を作成する方法

いくつかのパラメーターを持つストアドプロシージャがあります。特定のパラメータに値がある場合にのみ、特定のテーブルと結合するようにクエリを記述したいと思います。次の例を見てください。Personテーブルがあります。個人の住所を保持する住所テーブルと、個人グループを保持するグループテーブルもあります。どちらもPersonテーブルとの1対多の関係です。私のストアドプロシージャには、@ AddressIDパラメーターと@GroupIDパラメーターがあります。

クエリは常にPersonテーブルからフィールドを返すだけです。どちらのパラメーターにも値がない場合、クエリはPersonテーブルからすべてのレコードを返す必要があります。 @AddressIDパラメーターを指定すると、Addressテーブルに一致するレコードがあるレコードのみが返され、Groupsテーブルは無視されます。 @GroupIDパラメーターが指定されている場合、Groupsテーブルに一致するレコードがあるレコードのみを返し、Addressesテーブルを無視する必要があります。両方のパラメーターを指定すると、両方のテーブルに一致するレコードがあるレコードのみが表示されます。理にかなっていますか?

これを行う簡単な方法はありますか?

ありがとう、コーリー

44
Corey Burnett

私が正しく理解していれば、あなたの参加条件は
ON ((@AddressID IS NOT NULL) AND (alias.column = @AddressID))および同様にグループに参加します。

この条件付き結合を時々使用します。

44

簡単な方法は、実際には良い解決策ではありません。悪いことですが、最良の解決策は、コードに明示的な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 ...

この理由は、3つ(またはそれ以上)の条件すべてに一致する1つのクエリを作成しようとする場合、エンジンは1つsingleall条件で機能するクエリプラン。 T-SQLでは、1つのステートメントが1つのプランに相当します。 any変数値の一般的な場合の計画が作成されるため、結果は常に非常に悪い計画になることに注意してください。

Isは直観に反し、プログラマーにとって恐ろしい解決策のように見えますが、これがデータベースの仕組みです。 99.99%の頻度でこれが問題にならない理由は、あなたが尋ねたものを試し、何をすべきかを見た後、開発者がすぐに感覚に達し、要件を修正して、オプションで参加するクエリを実行する必要がないようにするためですランタイム変数値に基づいて;)

25
Remus Rusanu

はい、それは非常に簡単です。アドレスとグループの左結合を実行します。次に、where句で...

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

これは私が私のケースのために行った方法です。


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
Irawan Soetomo

これを拡張できるはずです...

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
3
Paul Creasey

左結合と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;
0
RayLoveless

Quntinが投稿したのは素晴らしいですが、パフォーマンスの問題がいくつかあります。信じられないかもしれませんが、より速いのは、各パラメーターをチェックし、ケースに基づいてSQL Joinを書くことです

加えて:

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

もう1つできることは、結合を実行し、クエリフィルタ(where句)でできることです。

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

ここでのパフォーマンスも日陰です。

0
JonH

おそらく、皆さんはこれまでにこれを解決しました。

私はあなたを理解しているように、パラメータが存在する場合はテーブルを結合し、パラメータが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がnullでない場合、外部結合は子をこの親と結合し、条件(@ConditionId IS NULL OR c.ConditionId = @ConditionId)は、条件付きc.ConditionId = @ConditionId

LEFT OUTER JOINには確かにパフォーマンスの問題がありますが、これが高速に機能する限り、クエリを作成するために文字列を連結したくありません。

0
vllado2

3つのテーブルを結合し、WHERE句で次のようなものを使用します。

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