Как вставить данные из таблицы и развернуть их - PullRequest
0 голосов
/ 12 сентября 2018

У меня есть 2 таблицы.

Таблица 1 имеет около 2 тыс. Столбцов, которые выглядят так:

Id  A B C D E F G H...........................................AA
1
2

Таблица 2 имеет 2 столбца, которые выглядят следующим образом:

Id Category
1  A
1  C
1  AA
2  B 
2  D  

Я хочу вставить и повернуть данные таблицы2 в таблице1

Таблица 1 должна наконец выглядеть как

Id  A B C D E F G H...........................................AA
1   1 0 1 0 0 0 0 0...........................................0 
2   0 1 1 0 0 0 0 0...........................................0

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

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

Мы можем использовать динамический sql для генерации сводного запроса с необходимыми столбцами и его выполнения.

Примечание: я использую NULL вместо '0' в пропущенных столбцах, чтобы не превышать допустимый максимальный разреженный размер данных в 8023 байта.

Если столбцы a, b, c ... AA из таблицы1:

  • int: он будет поддерживать около 800 полей со значением 1, а остальные - нулевые
  • бит: он будет поддерживать около 1142 полей со значением 1, а остальные - нулевые (если смотреть по вашему примеру, это лучший тип данных)

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

~~~~

SELECT MAX([fields])
FROM (SELECT [fields] = COUNT(DISTINCT [category]) FROM [table2] GROUP BY [id]) AS [t]

~~~~

Отредактировано:

  • процесс для партии размером 10000
  • предполагает, что любой идентификатор в таблице2 существует в таблице1

~~~~

DECLARE @target_schema_name SYSNAME = N'dbo'; -- Modify if necessary
DECLARE @target_table_name SYSNAME = N'table1'; -- Modify if necessary
DECLARE @target_table_identifier SYSNAME = N'id'; -- Modify if necessary

DECLARE @source_schema_name SYSNAME = N'dbo'; -- Modify if necessary
DECLARE @source_table_name SYSNAME = N'table2'; -- Modify if necessary
DECLARE @source_table_identifier SYSNAME = N'id'; -- Modify if necessary
DECLARE @source_table_pivoter SYSNAME = N'category'; -- Modify if necessary
DECLARE @source_table_subquery_columns NVARCHAR(MAX) =
    + '[src].' + QUOTENAME(@source_table_identifier)
    + ','
    + '[src].' + QUOTENAME(@source_table_pivoter)
    + ','
    + '[auxiliar] = 1';


DECLARE @columns TABLE([name] SYSNAME);

INSERT INTO @columns ([name])
SELECT [c].[name]
FROM [sys].[schemas] AS [s]
INNER JOIN [sys].[tables] AS [t]
    ON ([s].[schema_id] = [t].[schema_id])
INNER JOIN [sys].[columns] AS [c]
    ON ([t].[object_id] = [c].[object_id])
WHERE (1 = 1)
    AND ([s].[name] = @target_schema_name)
    AND ([t].[name] = @target_table_name)
    AND ([c].[name] <> @target_table_identifier)
    AND ([c].[is_column_set] <> 1);

DECLARE @target_table_pivot_columns NVARCHAR(MAX) = NULL;
DECLARE @target_table_insert_columns NVARCHAR(MAX) = NULL;

DECLARE @source_table_insert_columns NVARCHAR(MAX) = NULL;

SELECT
        @target_table_pivot_columns = IIF(@target_table_pivot_columns IS NULL, '', @target_table_pivot_columns + ',') + QUOTENAME([c].[name])
    ,@source_table_insert_columns = IIF(@source_table_insert_columns IS NULL, '', @source_table_insert_columns + ',') + QUOTENAME([c].[name]) --+ ' = ISNULL(' + QUOTENAME([c].[name]) + ', 0)'
FROM @columns AS [c];

SET @target_table_insert_columns = QUOTENAME(@target_table_identifier) + ',' + @target_table_pivot_columns;
SET @source_table_insert_columns = QUOTENAME(@source_table_identifier) + ',' + @source_table_insert_columns;

DECLARE @command NVARCHAR(MAX);
DECLARE @batch_min INT;
DECLARE @batch_max INT;

CREATE TABLE #ids ([id] INT, [batch] INT);

SET @command = '
INSERT INTO #ids ([id], [batch])
SELECT DISTINCT ' + QUOTENAME(@source_table_identifier) +  ', (ROW_NUMBER() OVER (ORDER BY (SELECT 1)) - 1) / 10000
FROM ' +  QUOTENAME(@source_schema_name) + '.' + QUOTENAME(@source_table_name) + ';
';

EXECUTE (@command);

SET @batch_min = 0;
SET @batch_max = (SELECT MAX([batch]) FROM #ids);

WHILE @batch_min <= @batch_max
BEGIN
    SET @command = '
    INSERT INTO ' + QUOTENAME(@target_schema_name) + '.' + QUOTENAME(@target_table_name) + '(' + @target_table_insert_columns + ')
    SELECT ' + @source_table_insert_columns + '
    FROM
    (
        SELECT ' + @source_table_subquery_columns + '
        FROM ' +  QUOTENAME(@source_schema_name) + '.' + QUOTENAME(@source_table_name) + ' AS [src]
        INNER JOIN #ids AS [ids]
            ON ([src].' + QUOTENAME(@source_table_identifier) + ' = [ids].[id])
        WHERE (1 = 1)
            AND ([ids].[batch] = ' + CONVERT(VARCHAR(11), @batch_min) + ')
    ) AS [src]
    PIVOT (MAX([auxiliar]) FOR [category] IN (' + @target_table_pivot_columns + ')) AS [pvt]
    ';

    EXECUTE (@command);

    SET @batch_min += 1;
END

~~~~

0 голосов
/ 13 сентября 2018

Абсолютно глупым в командах sql pivot является необходимость знать / перечислять каждый столбец.Почему-то в Microsoft Access есть очень элегантный способ поворота всего.Из-за этого я бы даже не стал использовать команду pivot.Intead просто сделайте массивный кейс и сгруппируйте по

select 
id, 
sum(case when category = 'A' then 1 else 0 end) as 'A',
sum(case when category = 'B' then 1 else 0 end) as 'B',
sum(case when category = 'C' then 1 else 0 end) as 'C',
etc...
from table2
group by id

Я согласен с некоторыми другими комментариями.Если ваша ситуация позволяет, рассмотрите возможность использования Excel PowerQuery с отличной командой pivot / unpivot или используйте инструмент на основе графического интерфейса, например Alteryx, с инструментами pivot / transpose.Гораздо быстрее!

...