2013-09-06 2 views
2

Я видел похожие вопросы, размещенные здесь, но либо я не получаю ответы, либо они не применяются ... вот что мне нужно и подумал, что это будет очень просто: У меня есть набор элементов, и каждый элемент имеет набор подэлементов. Количество подэлементов изменяется на каждый элемент. EG:Создание всех возможных комбинаций с SQL Server

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 

Я пробовал varietè внутренних соединений и кросс-соединений, но не смог заставить его работать. Я думаю, что логическая часть может быть добавлена ​​с использованием перекрестного соединения к таблице с двумя строками, которые имеют значения True и False, и я также думаю, что мне нужно сделать подзапрос «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 

таким образом, это подзапрос, что провал для меня. Любая помощь приветствуется!

+0

смотрите здесь: http://stackoverflow.com/questions/3621494/the-most-elegant-way-to-generate-permutations-in-sql-server – epoch

+0

@epoch Спасибо, но я не думаю это мой же случай ... Мне нужны все комбинации подэлементов на элемент, а апеллятору нужны все против всех ... –

+0

Каково максимальное количество подэлементов для элемента? Ваш результат может потенциально взорваться, так как количество комбинаций для n элементов будет 2^n. Например, 10 подэлементов приведут к 1024 комбинациям ... – Dan

ответ

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 

Edit после того, как думал об этом немного, я проверил это по сравнению с демпинг кучу информации в таблицу #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)); 

Вероятно, вы должны проверить оба варианта относительно ваших фактических данных/схемы.

Смежные вопросы