web-dev-qa-db-ja.com

左結合がnullを返さないのはなぜですか?

SQL Server 2008では、次のクエリがあります。

select      
    c.title as categorytitle,
    s.title as subcategorytitle,
    i.title as itemtitle
from categories c
join subcategories s on c.categoryid = s.categoryid
left join itemcategories ic on s.subcategoryid = ic.subcategoryid 
left join items i on ic.itemid = i.itemid and i.siteid = 132
where (ic.isactive = 1 or ic.isactive is null)
order by c.title, s.title

サブカテゴリのアイテムを取得しようとしていますが、カテゴリまたはサブカテゴリにアイテムがない場合でもレコードを返したいです。アイテムのないサブカテゴリは返されません。何が悪いのですか?

ありがとうございました

[〜#〜]編集[〜#〜]

2番目の左結合とwhere句を含む変更されたクエリですが、それでもnullは返されません。 :/

編集2

Siteidをアイテムの左結合に移動しました。これを行うと、予想よりもはるかに多くのレコードが取得されます。一部のアイテムにはnullのサイトIDがあり、特定のIDがある場合にのみ含めたいです。

編集

テーブル構造:

Categories Table 
-------
CategoryID
Title

SubCategories Table
-------
SubCategoryID
CategoryID
Title

ItemCategories Table
-------
ItemCategoryID
ItemID
SubCategoryID
IsActive

Items Table 
--------
ItemID
Title
SiteID
21
jimj

_join items i_...を_LEFT join items i_...に変更すると、クエリは期待どおりに機能するはずです。

[〜#〜]編集[〜#〜]
nullを考慮しない限り、where句でLEFT JOINテーブルをフィルタリングすることはできません。左結合では、一致する行がない場合にこれらの列に値を設定したり、nullにしたりできるためです。

_and i.siteid = 132_は、NULL _i.siteid_が存在する行がない場合、その行をすべて破棄します。これをオンに移動します。

_left join items i on ic.itemid = i.itemid and i.siteid = 132_

または、WHEREでNULLを処理します。

WHERE ... AND (i.siteid = 132 OR i.siteid IS NULL)

[〜#〜] edit [〜#〜] OPの編集に基づいて3

_SET NOCOUNT ON
DECLARE @Categories table (CategoryID int,Title varchar(30))
INSERT @Categories VALUES (1,'Cat AAA')
INSERT @Categories VALUES (2,'Cat BBB')
INSERT @Categories VALUES (3,'Cat CCC')

DECLARE @SubCategories table (SubCategoryID int,CategoryID int,Title varchar(30))
INSERT @SubCategories VALUES (1,1,'SubCat AAA A')
INSERT @SubCategories VALUES (2,1,'SubCat AAA B')
INSERT @SubCategories VALUES (3,1,'SubCat AAA C')
INSERT @SubCategories VALUES (4,2,'SubCat BBB A')

DECLARE @ItemCategories table (ItemCategoryID int, ItemID int, SubCategoryID int, IsActive char(1))
INSERT @ItemCategories VALUES (1,1,2,'Y')
INSERT @ItemCategories VALUES (2,2,2,'Y')
INSERT @ItemCategories VALUES (3,3,2,'Y')
INSERT @ItemCategories VALUES (4,4,2,'Y')
INSERT @ItemCategories VALUES (5,7,2,'Y')

DECLARE @Items table (ItemID int, Title varchar(30), SiteID int)
INSERT @Items VALUES (1,'Item A',111)
INSERT @Items VALUES (2,'Item B',111)
INSERT @Items VALUES (3,'Item C',132)
INSERT @Items VALUES (4,'Item D',111)
INSERT @Items VALUES (5,'Item E',111)
INSERT @Items VALUES (6,'Item F',132)
INSERT @Items VALUES (7,'Item G',132)
SET NOCOUNT OFF
_

OPが何であるかは100%わかりません。これは、質問で指定された_siteid=132_のときに参加できるすべての情報を返します

_SELECT
    c.title as categorytitle
        ,s.title as subcategorytitle
        ,i.title as itemtitle
        --,i.itemID, ic.SubCategoryID, s.CategoryID
    FROM @Items                          i
        LEFT OUTER JOIN @ItemCategories ic ON i.ItemID=ic.ItemID
        LEFT OUTER JOIN @SubCategories   s ON ic.SubCategoryID=s.SubCategoryID
        LEFT OUTER JOIN @Categories      c ON s.CategoryID=c.CategoryID
    WHERE i.siteid = 132
_

出力:

_categorytitle                  subcategorytitle               itemtitle
------------------------------ ------------------------------ ------------------------------
Cat AAA                        SubCat AAA B                   Item C
NULL                           NULL                           Item F
Cat AAA                        SubCat AAA B                   Item G

(3 row(s) affected)
_

これは、_siteid=132_に一致しない場合でも、すべてのカテゴリを一覧表示します

_;WITH AllItems AS
(
SELECT
    s.CategoryID, ic.SubCategoryID, ItemCategoryID, i.ItemID
        ,c.title AS categorytitle, s.title as subcategorytitle, i.title as itemtitle
    FROM @Items                          i
        LEFT OUTER JOIN @ItemCategories ic ON i.ItemID=ic.ItemID
        LEFT OUTER JOIN @SubCategories   s ON ic.SubCategoryID=s.SubCategoryID
        LEFT OUTER JOIN @Categories      c ON s.CategoryID=c.CategoryID
    WHERE i.siteid = 132
)
SELECT
    categorytitle, subcategorytitle,itemtitle
    FROM AllItems
UNION
SELECT
    c.Title, s.Title, null
    FROM @Categories                     c
        LEFT OUTER JOIN @SubCategories   s ON c.CategoryID=s.CategoryID
        LEFT OUTER JOIN @ItemCategories ic ON s.SubCategoryID=ic.SubCategoryID
        LEFT OUTER JOIN AllItems         i ON c.CategoryID=i.CategoryID AND  s.SubCategoryID=i.SubCategoryID
    WHERE i.ItemID IS NULL
ORDER BY categorytitle,subcategorytitle
_

出力:

_categorytitle                  subcategorytitle               itemtitle
------------------------------ ------------------------------ ------------------------------
NULL                           NULL                           Item F
Cat AAA                        SubCat AAA A                   NULL
Cat AAA                        SubCat AAA B                   Item C
Cat AAA                        SubCat AAA B                   Item G
Cat AAA                        SubCat AAA C                   NULL
Cat BBB                        SubCat BBB A                   NULL
Cat CCC                        NULL                           NULL

(7 row(s) affected)
_
34
KM.

I.siteidの「WHERE」基準は、出力に「items」行がなければならないことを意味します。書き込む(i.siteidがnullまたはi.siteid = 132)か、 "i.siteid = 132"を "ON"句に入力する必要があります。itemcategoriesの結合でも機能するもの:

select      
    c.title as categorytitle,
    s.title as subcategorytitle,
    i.title as itemtitle
from categories c
join subcategories s on c.categoryid = s.categoryid
left join itemcategories ic on s.subcategoryid = ic.subcategoryid and ic.isactive = 1
left join items i on ic.itemid = i.itemid and i.siteid = 132
order by c.title, s.title
7
araqnid

たぶん、この結合も左結合でなければなりませんか?

join items i on ic.itemid = i.itemid and i.siteid = 132

[〜#〜]編集[〜#〜]

ここで、where句で既存のサイトIDのみを選択しています。

i.siteid = 132

それはnull値を許可するはずです、このようなものを試してください:

(i.siteid = 132 or i.siteid is null)

または、i.siteid = 132結合条件に戻る

1
Andrew Bezzub
where (ic.isactive = 1 or ic.isactive is null) and i.siteid = 132

Where句にi.siteID = 132を含めると、アイテムの左結合を本質的に無効にします。

0
JupiterP5

2回目の試み、私は今あなたの問題を得たと思います。私が正しく理解している場合は、SiteIDに2種類のNULLが含まれることになります(10,000eの結果が表示されたが、それでも正しい方向に進んでいる場合)。

左結合からのNULLと実際のアイテムテーブルのサイトIDからのNULLがあります。あなたは左結合からのものを持ちたいが、データからのものは欲しくない。

これは、一致する行の存在をテストする際の外部結合でよくある間違いです。

NOT NULLとして定義されている列でのみNULLを常にテストする必要がある一致しない行が必要な場合は、ここで注意してください(ここでは外部テーブルの主キーが自然な候補です)。それ以外の場合、LEFT結合のためにNULLである行と、INNER結合であってもNULLになる行を区別できません。

これを行うには、少なくとも2つの方法があります。a)左結合が開始する前に、siteidを持つ行を除外するサブクエリの左結合がnullです。
b)次のように書き換え基準(ItemsにItemIDが必要であると想定)

select      
    c.title as categorytitle,
    s.title as subcategorytitle,
    i.title as itemtitle
from categories c
join subcategories s on c.categoryid = s.categoryid
left join itemcategories ic on s.subcategoryid = ic.subcategoryid 
left join items i on ic.itemid = i.itemid
where (ic.isactive = 1 or ic.isactive is null) AND (i.siteid = 132 or i.itemid is null)
order by c.title, s.title

(私は、joins to itemsテーブルまでのクエリが期待通りのものを提供していたと思います)。

Itemidがアイテムで必要な場合、上記の条件は-siteid 132の行、または実際には一致しない左結合から来る行です(条件はi.itemidがnullであり、i.siteidがnullではないことに注意してください)。

0
Unreason