Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/78.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL Server:从列到行_Sql_Sql Server_Tsql_Unpivot - Fatal编程技术网

SQL Server:从列到行

SQL Server:从列到行,sql,sql-server,tsql,unpivot,Sql,Sql Server,Tsql,Unpivot,正在寻找将列转换为行的优雅(或任何)解决方案 下面是一个示例:我有一个具有以下模式的表: [ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150] 以下是我希望得到的结果: [ID] [EntityId] [IndicatorName] [IndicatorValue] 结果值为: 1 1 'Indicator1' 'Value of Indicator 1 for entity 1' 2 1 'Ind

正在寻找将列转换为行的优雅(或任何)解决方案

下面是一个示例:我有一个具有以下模式的表:

[ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150]
以下是我希望得到的结果:

[ID] [EntityId] [IndicatorName] [IndicatorValue]
结果值为:

1 1 'Indicator1' 'Value of Indicator 1 for entity 1'
2 1 'Indicator2' 'Value of Indicator 2 for entity 1'
3 1 'Indicator3' 'Value of Indicator 3 for entity 1'
4 2 'Indicator1' 'Value of Indicator 1 for entity 2'
等等

这有意义吗?对于在T-SQL中查找和如何完成,您有什么建议吗

您可以使用该函数将列转换为行:

select id, entityId,
  indicatorname,
  indicatorvalue
from yourtable
unpivot
(
  indicatorvalue
  for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
注意,要取消PIVOT的列的数据类型必须相同,因此您可能必须在应用取消PIVOT之前转换数据类型

您还可以将
交叉应用
与UNION ALL一起使用来转换列:

select id, entityid,
  indicatorname,
  indicatorvalue
from yourtable
cross apply
(
  select 'Indicator1', Indicator1 union all
  select 'Indicator2', Indicator2 union all
  select 'Indicator3', Indicator3 union all
  select 'Indicator4', Indicator4 
) c (indicatorname, indicatorvalue);
根据您的SQL Server版本,您甚至可以使用带VALUES子句的交叉应用:

select id, entityid,
  indicatorname,
  indicatorvalue
from yourtable
cross apply
(
  values
  ('Indicator1', Indicator1),
  ('Indicator2', Indicator2),
  ('Indicator3', Indicator3),
  ('Indicator4', Indicator4)
) c (indicatorname, indicatorvalue);
最后,如果要取消PIVOT的列有150个,并且不想硬编码整个查询,那么可以使用动态sql生成sql语句:

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

select @colsUnpivot 
  = stuff((select ','+quotename(C.column_name)
           from information_schema.columns as C
           where C.table_name = 'yourtable' and
                 C.column_name like 'Indicator%'
           for xml path('')), 1, 1, '')

set @query 
  = 'select id, entityId,
        indicatorname,
        indicatorvalue
     from yourtable
     unpivot
     (
        indicatorvalue
        for indicatorname in ('+ @colsunpivot +')
     ) u'

exec sp_executesql @query;

如果你有150列,那么我认为UNPIVOT不是一个选项。所以您可以使用xml技巧

;with CTE1 as (
    select ID, EntityID, (select t.* for xml raw('row'), type) as Data
    from temp1 as t
), CTE2 as (
    select
         C.id, C.EntityID,
         F.C.value('local-name(.)', 'nvarchar(128)') as IndicatorName,
         F.C.value('.', 'nvarchar(max)') as IndicatorValue
    from CTE1 as c
        outer apply c.Data.nodes('row/@*') as F(C)
)
select * from CTE2 where IndicatorName like 'Indicator%'

您也可以编写动态SQL,但我更喜欢xml—对于动态SQL,您必须具有直接从表中选择数据的权限,而这并不总是一个选项

更新
由于评论中出现了激烈的争论,我想我将添加一些xml/动态SQL的优点和缺点。我会尽量客观,不要提优雅和丑陋。如果你有任何其他的优点和缺点,编辑答案或写评论

缺点

ID  Item    Value
1   Col1    A
1   Col2    B
2   Col1    R
2   Col2    C
3   Col1    X
3   Col2    D
  • 它不如动态SQL快,粗略的测试告诉我xml比动态SQL慢大约2.5倍(这是对250000行表的一次查询,所以这个估计是不精确的)。如果您愿意,您可以自己进行比较,这里的示例是,在100000行上,它是29s(xml)和14s(动态)
  • 可能对于不熟悉xpath的人来说,更难理解
专业人士

ID  Item    Value
1   Col1    A
1   Col2    B
2   Col1    R
2   Col2    C
3   Col1    X
3   Col2    D
  • 它的作用域与其他查询的作用域相同,这可能非常方便。我想到了几个例子
    • 您可以在触发器中查询
      插入的
      删除的
      (使用dynamic根本不可能)
    • 用户不必具有直接从表中选择的权限。我的意思是,如果您有存储过程层,并且用户有运行sp的权限,但没有直接查询表的权限,那么您仍然可以在存储过程中使用此查询
    • 您可以查询在您的作用域中填充的表变量(要将其传递到动态SQL中,您必须将其改为临时表,或者创建类型并将其作为参数传递到动态SQL中
  • 您可以在函数(标量或表值)内部执行此查询
声明@TableName varchar(max)=NULL
选择@TableName=COALESCE(@TableName+,,'')+t.TABLE_CATALOG+'.+t.TABLE_SCHEMA+'.+o.Name
从sysindex开始
将系统对象作为o在i.id=o.id上进行内部连接
内部联接信息\u SCHEMA.TABLES T ON T.TABLE\u NAME=o.NAME
其中i.indid<2
和OBJECTPROPERTY(o.id,'IsMSShipped')=0
和i.rowcnt>350
和o.xtype!='TF'
按o.name ASC订购
打印@tablename

您可以获得行数>350的表列表。您可以在表的解决方案列表中看到行。

我需要一个解决方案在Microsoft SQL Server中将列转换为行,而不知道列名称(在触发器中使用)和动态SQL(动态SQL在触发器中使用速度太慢)

我终于找到了这个解决方案,效果很好:

SELECT
    insRowTbl.PK,
    insRowTbl.Username,
    attr.insRow.value('local-name(.)', 'nvarchar(128)') as FieldName,
    attr.insRow.value('.', 'nvarchar(max)') as FieldValue 
FROM ( Select      
          i.ID as PK,
          i.LastModifiedBy as Username,
          convert(xml, (select i.* for xml raw)) as insRowCol
       FROM inserted as i
     ) as insRowTbl
CROSS APPLY insRowTbl.insRowCol.nodes('/row/@*') as attr(insRow)
如您所见,我将行转换为XML(子查询选择I,*对于XMLRAW,这将所有列转换为一个XML列)

然后,我对该列的每个XML属性交叉应用一个函数,以便每个属性得到一行

总的来说,这可以将列转换为行,而不需要知道列名,也不需要使用动态sql

(编辑:我刚刚看到上面罗曼·佩卡尔的回答,他也在做同样的事情。 我首先使用了带有游标的动态sql触发器,这比这个解决方案慢10到100倍,但可能是游标导致的,而不是动态sql。无论如何,这个解决方案非常简单(通用,所以它肯定是一个选项)


我把这篇评论留在这里,因为我想在我的帖子中引用关于完整审计触发的解释,你可以在这里找到:

为了帮助新读者,我创建了一个例子来更好地理解@BlueFoots关于UNPIVOT的回答

 SELECT id
        ,entityId
        ,indicatorname
        ,indicatorvalue
  FROM (VALUES
        (1, 1, 'Value of Indicator 1 for entity 1', 'Value of Indicator 2 for entity 1', 'Value of Indicator 3 for entity 1'),
        (2, 1, 'Value of Indicator 1 for entity 2', 'Value of Indicator 2 for entity 2', 'Value of Indicator 3 for entity 2'),
        (3, 1, 'Value of Indicator 1 for entity 3', 'Value of Indicator 2 for entity 3', 'Value of Indicator 3 for entity 3'),
        (4, 2, 'Value of Indicator 1 for entity 4', 'Value of Indicator 2 for entity 4', 'Value of Indicator 3 for entity 4')
       ) AS Category(ID, EntityId, Indicator1, Indicator2, Indicator3)
UNPIVOT
(
    indicatorvalue
    FOR indicatorname IN (Indicator1, Indicator2, Indicator3)
) UNPIV;

只是因为我没有看到它被提及

如果是2016+,这里还有另一个选项,可以在不实际使用动态SQL的情况下动态取消PIVOT数据

示例

Declare @YourTable Table ([ID] varchar(50),[Col1] varchar(50),[Col2] varchar(50))
Insert Into @YourTable Values 
 (1,'A','B')
,(2,'R','C')
,(3,'X','D')

Select A.[ID]
      ,Item  = B.[Key]
      ,Value = B.[Value]
 From  @YourTable A
 Cross Apply ( Select * 
                From  OpenJson((Select A.* For JSON Path,Without_Array_Wrapper )) 
                Where [Key] not in ('ID','Other','Columns','ToExclude')
             ) B
返回

ID  Item    Value
1   Col1    A
1   Col2    B
2   Col1    R
2   Col2    C
3   Col1    X
3   Col2    D

与此相反的是将列展平为csv eg

从字符串_SPLIT('Akio,Hiraku,Kazuo',',')中选择字符串_AGG([value],','))


您研究过了吗?最后是BlueFoot的解决方案。优雅而实用。非常感谢大家。您使用XML选择哪些不需要从表中选择数据的数据?例如,您可以决定不授予用户从表中选择数据的权限,而只授予使用表的存储过程的权限,所以我可以在过程中选择xml,但是如果我想使用动态SQL,我必须使用一些变通方法如果你想让你的用户能够执行代码,你必须给他们执行代码所需的任何访问权限。不要为了让你的答案听起来更好而编造不存在的需求(你也不必对相互竞争的答案发表评论就能看到你的答案——如果他们找到了答案,他们也可以找到你的答案)