2

ここに投稿された同様の質問を見たことがありますが、答えが得られないか、適用されません...これが私が必要とするものであり、それは本当に簡単だと思いました:私は一連のアイテムを持っており、各アイテムにはサブアイテムのセット。アイテムごとにサブアイテムの数が変わります。例えば:

Item 1
   SubItem 1-1
   SubItem 1-2
   SubItem 1-3
Item 2
   SubItem 2-1
Item 3
   SubItem 3-1
   SubItem 3-2

非常に具体的な用途のために、各アイテムのサブアイテムの可能な組み合わせごとにコメントを追加し、さらに各サブアイテムにブール値のプロパティを追加する必要があるため、次のようになります。

Item 1   Subitem 1-1 = True, Subitem 1-2 = True, Subitem 1-3 = True
Item 1   Subitem 1-1 = True, Subitem 1-2 = True, Subitem 1-3 = False
Item 1   Subitem 1-1 = True, Subitem 1-2 = False, Subitem 1-3 = True
Item 1   Subitem 1-1 = True, Subitem 1-2 = False, Subitem 1-3 = False
Item 1   Subitem 1-1 = False, Subitem 1-2 = True, Subitem 1-3 = True
... (the rest of Item 1 possible combinations)
Item 2   Subitem 2-1 = True
Item 2   Subitem 2-1 = False
Item 3   Subitem 3-1 = True, Subitem 3-2 = True
Item 3   Subitem 3-1 = True, Subitem 3-2 = False
Item 3   Subitem 3-1 = False, Subitem 3-2 = True
Item 3   Subitem 3-1 = False, Subitem 3-2 = False

内部結合とクロス結合のバリエーションを試しましたが、うまくいきませんでした。True と False の値を持つ 2 つの行を持つテーブルにクロス結合を使用してブール部分を追加できると思います。また、サブアイテムを 1 行で取得するには「FOR XML」サブクエリを実行する必要があると思いますが、サブアイテムの組み合わせを取得できません

これは私がこれまでに持っているものです:

-- Schema creation and data filling
DECLARE @Item TABLE (ItemId int, Name varchar(50))
DECLARE @Item_SubItem TABLE (ItemId int, SubitemId int)
DECLARE @SubItem TABLE (SubitemId int, Name varchar(50))

INSERT INTO @Item values (1, 'Item 1')
INSERT INTO @Item values (2, 'Item 2')
INSERT INTO @Item values (3, 'Item 3')
INSERT INTO @SubItem values (1, 'SubItem 1-1')
INSERT INTO @SubItem values (2, 'SubItem 1-2')
INSERT INTO @SubItem values (3, 'SubItem 1-3')
INSERT INTO @SubItem values (4, 'SubItem 2-1')
INSERT INTO @SubItem values (5, 'SubItem 3-1')
INSERT INTO @SubItem values (6, 'SubItem 3-2')
INSERT INTO @Item_SubItem values (1, 1)
INSERT INTO @Item_SubItem values (1, 2)
INSERT INTO @Item_SubItem values (1, 3)
INSERT INTO @Item_SubItem values (2, 4)
INSERT INTO @Item_SubItem values (3, 5)
INSERT INTO @Item_SubItem values (3, 6)

select I.Name, SI.Name
  from @Item I
       inner join @Item_SubItem ISI on ISI.ItemId = I.ItemId
       INNER JOIN @SubItem SI on SI.SubitemId = ISI.SubitemId
 order by I.Name, SI.Name


-- Actual query
SELECT ItemName = M.name, (SELECT iC.name + '=' + CASE AuxCode WHEN 1 THEN 'True' WHEN 0 THEN 'False' END + ' '
                                   FROM Item_subitem AS iCGM
                                        INNER JOIN Subitem AS iC ON iC.SubitemId = iCGM.SubitemId
                                        CROSS JOIN (SELECT AuxCode = 1 UNION SELECT AuxCode = 0) Aux
                                  WHERE iCGM.ItemId = M.ItemId
                                  ORDER BY iC.name
                                    FOR XML PATH(''))
  FROM Item M

だから、私にとって失敗しているのはサブクエリです。どんな助けでも大歓迎です!

4

1 に答える 1

3

最大 4 つのレベルを取得する方法は次のとおりです (サンプル データには 3 つしか必要ありませんが、これがそれを超えて機能することを確認したかったのです)。パターンに従って、7、10、何を持っているかを確認できるはずです。ああ、これが速いとは思わないでください。

;WITH z AS 
(
  SELECT i,inm,si,snm,truth,c FROM 
  (
    SELECT i = i.ItemId, inm = i.Name, si = isi.SubItemId, snm = s.Name,
      c = COUNT(isi.SubItemId) OVER (PARTITION BY i.ItemId)
    FROM @Item_SubItem AS isi
    INNER JOIN @Item AS i    ON isi.ItemId    = i.ItemId
    INNER JOIN @SubItem AS s ON isi.SubItemId = s.SubItemId
  ) AS y
  CROSS JOIN (VALUES('true'),('false')) AS t(truth)
)
SELECT Item = z1.inm, 
  SubItems = COALESCE(       z1.snm + ' = ' + z1.truth,'')
           + COALESCE(', ' + z2.snm + ' = ' + z2.truth,'')
           + COALESCE(', ' + z3.snm + ' = ' + z3.truth,'')
           + COALESCE(', ' + z4.snm + ' = ' + z4.truth,'')
FROM z AS z1
  LEFT OUTER JOIN z AS z2 
    ON z1.i = z2.i AND z1.si < z2.si
  LEFT OUTER JOIN z AS z3 
    ON z2.i = z3.i AND z2.si < z3.si 
  LEFT OUTER JOIN z AS z4 
    ON z3.i = z4.i AND z3.si < z4.si 
  WHERE (z1.c = 1) 
    OR (z1.c = 2 AND z2.i IS NOT NULL)
    OR (z1.c = 3 AND z3.i IS NOT NULL)
    OR (z1.c = 4 AND z4.i IS NOT NULL);

与えられたサンプル データの結果:

Item        SubItems
------      ---------------------------------------------------------------
Item 1      SubItem 1-1 = true, SubItem 1-2 = true, SubItem 1-3 = true
Item 1      SubItem 1-1 = true, SubItem 1-2 = true, SubItem 1-3 = false
Item 1      SubItem 1-1 = true, SubItem 1-2 = false, SubItem 1-3 = true
Item 1      SubItem 1-1 = true, SubItem 1-2 = false, SubItem 1-3 = false
Item 1      SubItem 1-1 = false, SubItem 1-2 = true, SubItem 1-3 = true
Item 1      SubItem 1-1 = false, SubItem 1-2 = true, SubItem 1-3 = false
Item 1      SubItem 1-1 = false, SubItem 1-2 = false, SubItem 1-3 = true
Item 1      SubItem 1-1 = false, SubItem 1-2 = false, SubItem 1-3 = false
Item 2      SubItem 2-1 = true
Item 2      SubItem 2-1 = false
Item 3      SubItem 3-1 = true, SubItem 3-2 = true
Item 3      SubItem 3-1 = true, SubItem 3-2 = false
Item 3      SubItem 3-1 = false, SubItem 3-2 = true
Item 3      SubItem 3-1 = false, SubItem 3-2 = false

これについて少し考えた後、編集して、最初に大量の情報を #temp テーブルにダンプする場合と比較してこれをテストしました。これは、順序が異なりますが、最適化されているようです (まだ順序付けされてItemIdいますが、偽の値はより高く並べ替えられます)。 :

SELECT c.i, c.inm, c.si, c.snm, c.c, t.truth 
INTO #x
FROM 
(
  SELECT 
     i = i.ItemId, inm = i.Name, si = isi.SubItemId, snm = s.Name,
     c = COUNT(isi.SubItemId) OVER (PARTITION BY i.ItemId)
  FROM @Item_SubItem AS isi
  INNER JOIN @Item AS i    ON isi.ItemId    = i.ItemId
  INNER JOIN @SubItem AS s ON isi.SubItemId = s.SubItemId
) AS c
CROSS JOIN (VALUES('true'),('false')) AS t(truth);

CREATE UNIQUE CLUSTERED INDEX x ON #x(i,si,truth);

SELECT 
  Item = z1.inm, 
  SubItems = COALESCE(       z1.snm + ' = ' + z1.truth,'')
           + COALESCE(', ' + z2.snm + ' = ' + z2.truth,'')
           + COALESCE(', ' + z3.snm + ' = ' + z3.truth,'')
           + COALESCE(', ' + z4.snm + ' = ' + z4.truth,'')
FROM #x AS z1
  LEFT OUTER JOIN #x AS z2 ON z1.i = z2.i AND z1.si < z2.si
  LEFT OUTER JOIN #x AS z3 ON z2.i = z3.i AND z2.si < z3.si
  LEFT OUTER JOIN #x AS z4 ON z3.i = z4.i AND z3.si < z4.si
WHERE (z1.c = 1) 
  OR (z1.c = 2 AND z2.i IS NOT NULL)
  OR (z1.c = 3 AND z3.i IS NOT NULL)
  OR (z1.c = 4 AND z4.i IS NOT NULL);

DROP TABLE #x;

基になるテーブルがインデックス化されている場合、元のバージョンははるかに有利です。

DECLARE @Item TABLE (ItemId int PRIMARY KEY, Name varchar(50));

DECLARE @Item_SubItem TABLE (ItemId int, SubitemId int, 
  PRIMARY KEY (ItemId,SubItemId));

DECLARE @SubItem TABLE (SubitemId int PRIMARY KEY, Name varchar(50));

おそらく、実際のデータ/スキーマに対して両方のバリエーションをテストする必要があります。

于 2013-09-06T22:32:04.183 に答える