Реструктуризация таблицы путем удаления значений NULL - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть таблица в SQL, которая выглядит так:

Customer   Product    1999     2000     2001    2002      2003
Smith      51         NULL     NULL       15      14      NULL
Jones      14           11        7     NULL    NULL      NULL
Jackson    13         NULL     NULL     NULL       3         9

Цифры в столбце каждого года представляют собой суммы в долларах. У каждого клиента есть два года подряд сумм, а остальные годы равны нулю. Я хотел бы изменить структуру этой таблицы, чтобы вместо широкого списка лет в ней были только два столбца Amount-Year1 и Amount-Year2. Таким образом, он выбирает два ненулевых года и помещает их в эти столбцы в правильном порядке. Это значительно уменьшит размер моего стола.

До сих пор я был в состоянии изменить его структуру так, чтобы в нем был один столбец суммы и один столбец года, но затем я получил несколько строк на клиента, чего, к сожалению, у меня не было (из-за последующего анализа). Кто-нибудь может придумать способ получить две колонки Amount-Year?

Я бы хотел, чтобы финальный стол выглядел так:

Customer    Product    Amount_Y1    Amount_Y2
Smith       51         15           14
Jones       14         11            7
Jackson     13          3            9

Я не против, чтобы я потерял информацию о конкретных годах, поскольку я могу получить ее из другого источника. Фактическая таблица содержит данные за все годы между 1999 и 2018 годами, и в будущем будут еще годы.

Спасибо

Ответы [ 4 ]

0 голосов
/ 30 апреля 2018

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

Пример запроса: (Предполагается, что table будет table1, а имена столбцов будут такими же, как год).

    DECLARE @columnsdesc nvarchar(max), @columnsasc nvarchar(max)
SET @columnsdesc = ''
SELECT @columnsdesc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     sys.columns c 
         JOIN sys.objects o ON o.object_id = c.object_id 
WHERE    o.type = 'U' and o.Name = 'table1' and c.Name not in ('Customer', 'Product')
ORDER BY c.Name desc for xml path ( '' ))

SET @columnsasc = ''
SELECT @columnsasc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     sys.columns c 
         JOIN sys.objects o ON o.object_id = c.object_id 
WHERE    o.type = 'U' and o.Name = 'table1' and c.Name not in ('Customer', 'Product')
ORDER BY c.Name asc for xml path ( '' ))
   SELECT @columnsasc = LEFT( @columnsasc,LEN(@columnsasc)-1)
   SELECT @columnsdesc = LEFT( @columnsdesc,LEN(@columnsdesc)-1)
   DECLARE @sql nvarchar(max)

SET @sql = 'SELECT Customer, Product, COALESCE('+ @columnsasc  +') as Amount_Y1,
COALESCE(' + @columnsdesc +' ) as Amount_Y2
FROM Table1'

EXEC(@sql)

Если вы имеете дело с temporary table, то код немного изменится: Проверьте это здесь: http://rextester.com/MRVR48808

DECLARE @columnsdesc nvarchar(max), @columnsasc nvarchar(max)
SET @columnsdesc = ''
SELECT @columnsdesc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     tempdb.sys.columns c               --Changes here
         JOIN tempdb.sys.objects o ON o.object_id = c.object_id    --Changes here
WHERE    o.type = 'U' and o.Name like '#table1%' and c.Name not in ('Customer', 'Product')   --Changes here
ORDER BY c.Name desc for xml path ( '' ))

SET @columnsasc = ''
SELECT @columnsasc = (select + '[' +  ltrim(c.Name)  +   ']' + ','
FROM     tempdb.sys.columns c                   --Changes here
         JOIN tempdb.sys.objects o ON o.object_id = c.object_id     --Changes here 
WHERE    o.type = 'U' and o.Name like '#table1%' and c.Name not in ('Customer', 'Product')  --Changes here
ORDER BY c.Name asc for xml path ( '' ))
   SELECT @columnsasc = LEFT( @columnsasc,LEN(@columnsasc)-1)
   SELECT @columnsdesc = LEFT( @columnsdesc,LEN(@columnsdesc)-1)
   DECLARE @sql nvarchar(max)

SET @sql = 'SELECT Customer, Product, COALESCE('+ @columnsasc  +') as Amount_Y1,
COALESCE(' + @columnsdesc +' ) as Amount_Y2
FROM #Table1'   --Changes here

EXEC(@sql)
0 голосов
/ 30 апреля 2018

Попробуйте использовать COALESCE следующим образом: для одного поля от начала до конца и для второго наоборот.

SELECT Customer,Product, COALESCE([1999],[2000],[2001],[2002],[2003]) as Y1, 
       COALESCE([2003],[2002],[2001],[2000],[1999])  as Y2
FROM  #TEMPDATA
0 голосов
/ 30 апреля 2018

Я бы сделал это, используя cross apply:

select t.customer, t.product, v.Amount_Y1, v.Amount_Y2
from t cross apply
     (select max(case when which = 1 then val end) as Amount_Y1,
             max(case when which = 2 then val end) as Amount_Y2
      from (select val, yr, row_number() over (order by yr) as which
            from (values (t.[1999], 1999), (t.[2000], 2000), (t.[2001], 2001),
                         (t.[2002], 2002), (t.[2003], 2003)
                 ) v(val, yr)
            where val is not null
           ) v
0 голосов
/ 30 апреля 2018

К счастью UNPIVOT удаляет NULL s в любом случае, поэтому мы можем сделать это с UNPIVOT / ROWNUMBER(), PIVOT:

declare @t table (Customer varchar(15),Product int,[1999] int,
                  [2000] int,[2001] int,[2002] int,[2003] int)
insert into @T(CUstomer,Product,[1999],[2000],[2001],[2002],[2003]) values
('Smith'  ,51,NULL,NULL,  15,  14,NULL),
('Jones'  ,14,  11,   7,NULL,NULL,NULL),
('Jackson',13,NULL,NULL,NULL,   3,   9)

;With Numbered as (
    select
        Customer,Product,Value,
        ROW_NUMBER() OVER (PARTITION BY Customer,Product
                           ORDER BY Year) rn
    from
        @t t
            unpivot
        (Value for Year in ([1999],[2000],[2001],[2002],[2003])) u
)
select
    *
from
    Numbered n
        pivot
    (SUM(Value) for rn in ([1],[2])) w

Результаты:

Customer        Product     1           2
--------------- ----------- ----------- -----------
Jackson         13          3           9
Jones           14          11          7
Smith           51          15          14
...