SQL Server получает все комбинации в таблице отношений без циклов и курсоров - PullRequest
0 голосов
/ 06 ноября 2019

У меня есть две таблицы, одна из которых называется Вариации , которая содержит вариации, такие как цвета , размеры и т. Д ....,

Идругая таблица атрибуты вариации , которая имеет значения этих вариаций.

Элемент может иметь n вариантов, например (Цвет и Размер) или (Цвет, Размер и Длина).

Мне нужно сгенерировать все возможные комбинации для значений Вариации, как показано ниже:

Вариации

Id  |Value
1   | Color
2   | Size
3   | Length

Атрибуты вариации

Id  | VariationId | Value
1   | 1           | Black
2   | 1           | Red
3   | 2           | Large
4   | 2           | Small
5   | 2           | Medium
6   | 3           | Tall

Все комбинации

Id  | VariationId | VariationAttrubuteId 
1   | 1 (Color)   | 1 (Black)
1   | 2 (Size)    | 3 (Large)
1   | 3 (Length)  | 6 (Tall)
**
2   | 1 (Color)   | 2 (Red)
2   | 2 (Size)    | 4 (Large)
2   | 3 (Length)  | 6 (Tall)

** И т. Д.

Можно ли заполнить таблицу всех комбинаций без петли.

Обновление: Добавлена ​​дополнительная информация

Уже выполнено внутреннее соединение, но это не вернет все комбинации, как описано выше. Также перекрестное объединение не будет работать, так как будет возвращать несвязанные данные. мне нужно найти способ сгруппировать вариации.

Уравнение: вариации D1 * вариации D2 * вариации D3 * вариации Dn. Так что каждая комбинация уникальна.

Ответы [ 3 ]

1 голос
/ 06 ноября 2019

Просто используйте CROSS JOIN .

create table Variations
(
    Id int not null,
    Value varchar(50) not null
)

create table Variation_Attributes
(
    Id int not null,
    VariationId int not null,
    Value varchar(50) not null
)
GO

insert into Variations
(Id, Value)
values
(1   , 'Color'),
(2   , 'Size'),
(3   , 'Length');

insert into Variation_Attributes
(Id  , VariationId , Value)
values
(1   , 1           , 'Black'),
(2   , 1           , 'Red'),
(3   , 2           , 'Large'),
(4   , 2           , 'Small'),
(5   , 2           , 'Medium'),
(6   , 3           , 'Tall');
GO


select *
 from Variations
 cross join Variation_Attributes

ОБНОВЛЕНИЕ

После выпуска ОП мы можем лучше оценитьвопрос и сама проблема. Это плохая дизайнерская проблема, ведущая к сложному решению. Лучшим решением может быть перепроектирование таблиц. Таблица для каждого типа имущества может работать лучше здесь Color, Size Length.

С другой стороны, если вы должны дать атрибуты объекту, скажем, это магазин, который продает электрические устройства для кухни, так что выпонадобится таблица отношений для каждого продукта и его возможные атрибуты, которые «решают» проблему.

Здесь указывается «Решает», почему вполне возможно, что настоящая проблема - это не проблема, которую пытается решить ОП. Очень распространенная проблема в ИТ-индустрии.

ОБНОВЛЕНИЕ 2

Когда кто-то называет карту "Это традиционное", вы мало что можете сделать.

Конечно, решение является тривиальным для фиксированного числа Variation.

select v0.Value, v1.Value, v2.Value
 from Variation_Attributes v0
 join Variation_Attributes v1 on v1.Id != v0.Id
 join Variation_Attributes v2 on v1.Id != v0.Id and v2.Id != v1.Id
where v0.VariationId = 1
and   v1.VariationId = 2
and   v2.VariationId = 3

, оно дает нам все шесть возможностей.

Но для динамического сценария OP должен использовать PIVOT илипостроить запрос динамически. Пример:

declare @index int = 0, @select varchar(max), @from varchar(max), @where varchar(max), @VariationId int;
declare MyLoop cursor fast_forward for (select Id from Variations);
open MyLoop;
fetch next from MyLoop into @VariationId
while @@FETCH_STATUS != -1
begin

    if (@index = 0)
    begin
        set @select = 'select v'+cast(@index as varchar)+'.Value as v'+cast(@index as varchar);
        set @from   = 'from Variation_Attributes v'+cast(@index as varchar);
        set @where  = 'where v'+cast(@index as varchar)+'.VariationId = '+cast(@VariationId as varchar);
    end
    else begin
        set @select = @select + ', v'+cast(@index as varchar)+'.Value as v'+cast(@index as varchar);
        set @from   = @from   + ' cross join Variation_Attributes v'+cast(@index as varchar);
        set @where  = @where  + ' and v'+cast(@index as varchar)+'.VariationId = '+cast(@VariationId as varchar);
    end

    set @index = @index + 1;

    fetch next from MyLoop into @VariationId;
end

--print @select;
--print @from;
--print @where;

close MyLoop;
deallocate MyLoop;

exec (@select+' '+@from+' '+@where);

Для данных примера он дает

v0      v1      v2
------- ------- -------
Black   Large   Tall
Black   Small   Tall
Black   Medium  Tall
Red     Large   Tall
Red     Small   Tall
Red     Medium  Tall
0 голосов
/ 07 ноября 2019

Единственный возможный способ, который я нашел, - это использование динамического SQL в сочетании с перекрестным соединением и циклом.

DECLARE @sql NVARCHAR(MAX) = '',
        @where NVARCHAR(MAX) = '',
        @index INT = 0, @currentVariation INT = 0;

-- Store all variation id's for a specific item
SELECT sv.VariationId VariationId
INTO #vars
FROM StockVariations sv 
WHERE StockId = @someId
ORDER BY sv.VariationId ASC;

-- Loop through all variations
DECLARE curr Cursor FOR SELECT VariationId FROM #vars;
OPEN curr;
FETCH NEXT FROM curr INTO @currentVariation
WHILE @@Fetch_Status <> -1
BEGIN
    IF @index > 0
    BEGIN
        SET @sql = @sql + ' CROSS JOIN';
    END
    -- Append Each variation as a table to the Sql Script
    SET @sql = @sql + ' (SELECT * FROM VariationAttributes WHERE VariationId IN (SELECT VariationId FROM #vars)) tb' + CONVERT(NVARCHAR, @index);
    IF @where <> ''
    BEGIN 
        SET @where = @where + ' AND';
    END
    SET @where = @where + ' tb' + CONVERT(NVARCHAR, @index) + '.VariationId = ' + CONVERT(NVARCHAR, @currentVariation);
    SET @index = @index + 1;
    FETCH NEXT FROM curr INTO @currentVariation;
END

PRINT 'SELECT * FROM ' + @sql + ' WHERE ' + @where;
CLOSE curr;
DEALLOCATE curr;

EXEC ('SELECT * FROM ' + @sql + ' WHERE ' + @where);
DROP TABLE #vars
0 голосов
/ 06 ноября 2019

Получить все комбинации в SQL легко - все, что вам нужно сделать, это перекрестное соединение:

SELECT Variations.Id, Variations.Value, VariationAttributes.Id, VariationAttributes.Value
FROM Variations
CROSS JOIN VariationAttributes

Однако, это обеспечит значения, которые не должны существовать, такие как Size Black и Color Large. Вы, вероятно, хотите вместо этого внутреннего соединения:

SELECT Variations.Id, Variations.Value, VariationAttributes.Id, VariationAttributes.Value
FROM Variations
INNER JOIN VariationAttributes
    ON Variations.Id = VariationAttributes.VariationId
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...