动态转换表不同的数据类型

时间:2017-06-10 00:24:26

标签: sql tsql

我发现这个很棒的帖子用于在sql中转换表:

Simple way to transpose columns and rows in Sql?

编辑:

input:
        Paul     | John  | Tim  |  Eric
Red    'hi'      |   5   |    1 |   3.3
Green  'there'   |   4   |    3 |   5.5
Blue   'everyone'|   2   |    9 |   7.5

预期产出:

       Red   |  Green   | Blue
Paul   'hi'  | 'there'  | 'everyone'
John    5    |   4      |   2
Tim     1    |   3      |   9
Eric    3.3  |   5.5    |   7.5

我想为动态不同数据类型的表使用最后一个动态解决方案:

CREATE TABLE yourTable([color] nvarchar(5), [Paul] nvarchar(10), [John] int, [Tim] 
int, [Eric] float);

INSERT INTO yourTable
([color], [Paul], [John], [Tim], [Eric])
VALUES
('Red', 'hi', 5, 1, 3.3),
('Green', 'there', 4, 3, 5.5),
('Blue', 'everyone', 2, 9, 7.5);

当我运行上一个答案中的代码时:

DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query  AS NVARCHAR(MAX),
@colsPivot as  NVARCHAR(MAX)

select @colsUnpivot = stuff((select ','+quotename(C.name)
     from sys.columns as C
     where C.object_id = object_id('yourtable') and
           C.name <> 'color'
     for xml path('')), 1, 1, '')

select @colsPivot = STUFF((SELECT  ',' 
                  + quotename(color)
                from yourtable t
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')


set @query 
= 'select name, '+@colsPivot+'
  from
  (
    select color, name, value
    from yourtable
    unpivot
    (
      value for name in ('+@colsUnpivot+')
    ) unpiv
  ) src
  pivot
  (
    sum(value)
    for color in ('+@colsPivot+')
  ) piv'

exec(@query)

当我运行此代码时,我收到错误消息:

“John”列的类型与UNPIVOT列表中指定的其他列的类型冲突。

有没有办法可以在我的桌子上使用这个动态解决方案而不会失去它的动态性质?我想理想地将一堆表传递给这个方法,以便批量转置它们。

由于

2 个答案:

答案 0 :(得分:0)

解决此问题的方法是使用数据类型SQL_VARIANT,以便生成的列可以处理多个数据类型。但是,您不能使用SUM()SQL_VARIANT列,因此sum(value)必须更改为max(value) - 或min(value) - 但对于此pivot,更改不会更改结果。

DECLARE @colsConvert AS NVARCHAR(MAX)
DECLARE @colsUnpivot AS NVARCHAR(MAX)
DECLARE @colsPivot as  NVARCHAR(MAX)
DECLARE @query  AS NVARCHAR(MAX)


select @colsConvert = (select ', cast('+quotename(C.name)+' as sql_variant) as '+quotename(C.name)
     from sys.columns as C
     where C.object_id = object_id('yourtable') and
           C.name <> 'color'
     for xml path(''))
     
select @colsUnpivot = stuff((select ','+quotename(C.name)
     from sys.columns as C
     where C.object_id = object_id('yourtable') and
           C.name <> 'color'
     for xml path('')), 1, 1, '')

select @colsPivot = STUFF((SELECT  ',' 
                  + quotename(color)
                from yourtable t
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')


set @query 
= 'select name, '+@colsPivot+'
  from
  (
    select color, name, value
    from (select color'+@colsConvert+' from yourtable) as converted
    unpivot
    (
      value for name in ('+@colsUnpivot+')
    ) unpiv
  ) src
  pivot
  (
    max(value)
    for color in ('+@colsPivot+')
  ) piv'

exec(@query)

请参阅此处:http://rextester.com/IBSN39688

结果:

+------+-----+-------+----------+
| name | Red | Green |   Blue   |
+------+-----+-------+----------+
| Eric | 3.3 | 5.5   | 7.5      |
| John | 5   | 4     | 2        |
| Paul | hi  | there | everyone |
| Tim  | 1   | 3     | 9        |
+------+-----+-------+----------+

生成的SQL:

select name, [Red],[Green],[Blue]
  from
  (
    select color, name, value
    from (select color, cast([Eric] as sql_variant) as [Eric], cast([John] as sql_variant) as [John], cast([Paul] as sql_variant) as [Paul], cast([Tim] as sql_variant) as [Tim] from yourtable) as converted
    unpivot
    (
      value for name in ([Eric],[John],[Paul],[Tim])
    ) unpiv
  ) src
  pivot
  (
    max(value)
    for color in ([Red],[Green],[Blue])
  ) piv

<强> +修改

在结果列中使用SQL_VARIANT的另一个好处是遇到的每种标准数据类型都将采用其默认格式。特别适用于十进制/浮点数和日期/时间数据。您还可以在运行动态数据透视之前修改默认值,以进一步影响输出。

Demonstrated here

答案 1 :(得分:0)

以下内容几乎可以转换任何表,视图或查询,同时遵循行和列序列。

完全披露:有一个主要缺点。这种方法不能很好地处理NULL值。 NULL值将导致以下列向左移动。

示例

$dataThisWeek = Data::where(\DB::raw("WEEKOFYEAR(created_at)"), $dt->weekOfYear)->get();

<强>返回

Declare @YourTable Table ([Color] varchar(50),[Paul] varchar(50),[John] int,[Tim] int,[Eric] decimal(10,1))
Insert Into @YourTable Values
 ('Red','hi',5,1,3.3)
,('Green','there',4,3,5.5)
,('Blue','everyone',2,9,7.5)

Declare @XML xml = (Select *,RowNr=Row_Number() over (Order By (Select NULL)) From  @YourTable for XML RAW)

Select RowNr = Row_Number() over(Partition By r.value('@RowNr','int') Order By (Select null))
      ,ColNr = r.value('@RowNr','int')
      ,Item  = attr.value('local-name(.)','varchar(100)')
      ,Value = attr.value('.','varchar(max)') 
 Into  #Temp
 From  @XML.nodes('/row') as XN(r)
 Cross Apply XN.r.nodes('./@*') AS XA(attr)
 Where attr.value('local-name(.)','varchar(100)') not in ('RowNr')

Declare @SQL varchar(max) = '
Select [Item],' + Stuff((Select Distinct ',' + QuoteName(ColNr)+' as '+QuoteName(Value) From #Temp Where RowNr=1 Order by 1 For XML Path('')),1,1,'') + '
 From  #Temp 
 Pivot (max([Value]) For [ColNr] in (' + Stuff((Select Distinct ',' + QuoteName(ColNr) From #Temp Order by 1 For XML Path('')),1,1,'') + ') ) p 
 Where RowNr>1
 Order By RowNr'
Exec(@SQL);

<强> dbFiddle