SQL 服务器:如何将行转换为列

SQL Server: How to convert rows to columns

我有一个 table,我在其中存储列名及其值代码和客户端。

这是我的 table 数据。附上屏幕截图。

我在这里尝试了这个 sql,它会抛出字段名称中重复值的错误。我从这个 post

得到了这段代码

但是他们的代码不适用于我的场景。请指导我需要更改的代码。

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

select @cols = STUFF((SELECT ',' + QUOTENAME(FieldName) 
                    from DynamicForm WHERE Ticker='X' AND ClientCode='Z'
                    group by FieldName, id,Ticker,ClientCode
                    order by id
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = N'SELECT ' + @cols + N' from 
             (
                select value, FieldName
                from DynamicForm WHERE Ticker=''X'' AND ClientCode=''Z''
            ) x
            pivot 
            (
                max(value)
                for FieldName in (' + @cols + N')
            ) p '

exec sp_executesql @query;

输出看起来像

+-------------+----------------------+-----------------+
| Last Update |        Broker        |     Analyst     |
+-------------+----------------------+-----------------+
| 7/6/2021    |    JMP Securities    | David M Scharf  |
| 4/28/2021   |  Argus Research Corp | David E Coleman |
+-------------+----------------------+-----------------+  

看到这里有两条记录,JMP Securities 获得第一条记录,因为它的 orderid 是 1。因此数据应该按照 Ticker 和客户端代码明智地显示,并且 orderId 明智的数据应该是有序的。

这是帮助您获取数据的脚本。

CREATE TABLE [dbo].[DynamicForm](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [FieldName] [varchar](100) NULL,
    [Value] [varchar](100) NULL,
    [Ticker] [varchar](10) NULL,
    [ClientCode] [varchar](10) NULL,
    [Order] [int] NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[DynamicForm] ON 
GO
INSERT [dbo].[DynamicForm] ([ID], [FieldName], [Value], [Ticker], [ClientCode], [Order]) VALUES (1, N'Last Update
', N'4/28/2021
', N'X', N'Z', 1)
GO
INSERT [dbo].[DynamicForm] ([ID], [FieldName], [Value], [Ticker], [ClientCode], [Order]) VALUES (2, N'Broker
', N'Argus Research Corp
', N'X', N'Z', 1)
GO
INSERT [dbo].[DynamicForm] ([ID], [FieldName], [Value], [Ticker], [ClientCode], [Order]) VALUES (3, N'Analyst 
', N'David E Coleman
', N'X', N'Z', 1)
GO
INSERT [dbo].[DynamicForm] ([ID], [FieldName], [Value], [Ticker], [ClientCode], [Order]) VALUES (4, N'Last Update
', N'7/6/2021
', N'X', N'Z', 2)
GO
INSERT [dbo].[DynamicForm] ([ID], [FieldName], [Value], [Ticker], [ClientCode], [Order]) VALUES (5, N'Broker
', N'JMP Securities
', N'X', N'Z', 2)
GO
INSERT [dbo].[DynamicForm] ([ID], [FieldName], [Value], [Ticker], [ClientCode], [Order]) VALUES (6, N'Analyst 
', N'David M Scharf
', N'X', N'Z', 2)
GO
SET IDENTITY_INSERT [dbo].[DynamicForm] OFF
GO

Here i tried this sql which is throwing error for duplicate values in field name.

这是因为您的 GROUP BYFieldName, id,Ticker,ClientCode 上。因此,您告诉 RDBMS 您想要为这些列的 every distinct 组排一行,很明显,这将导致相同的多行FieldName.

的值

很可能 GROUP BYORDER BY 根本不应该存在:

SELECT @cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(FieldName) 
                      FROM dbo.DynamicForm
                      WHERE Ticker='X'
                        AND ClientCode='Z'
                      FOR XML PATH(''), TYPE).value('(./text())[1]', 'nvarchar(MAX)') ,1,1,'');

现在我们有了示例数据,我可以提供完整的解决方案。就个人而言,我也会使用条件聚合,而不是限制性 PIVOT 运算符,并一次构建我的整个语句。我继续使用 FOR XML PATH,因为我 假设 你使用它(而不是 STRING_AGG),因为在 SQL Server 2016 或更早版本上。

DECLARE @SQL nvarchar(MAX),
        @CRLF nchar(2) = NCHAR(13) + NCHAR(10);

SET @SQL = N'SELECT ' + STUFF((SELECT N',' + @CRLF + N'       ' +
                                      N'MAX(CASE FieldName WHEN ' + QUOTENAME(FieldName,'''') + N' THEN Value END) AS ' + QUOTENAME(FieldName)
                               FROM dbo.DynamicForm
                               GROUP BY FieldName
                               ORDER BY MIN(ID)
                               FOR XML PATH(''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,10,N'') + @CRLF +
           N'FROM dbo.DynamicForm' + @CRLF +
           N'WHERE Ticker = @Ticker' + @CRLF +
           N'  AND ClientCode = @ClientCode' + @CRLF + 
           N'GROUP BY [Order]' + @CRLF + --ORDER is a reserved keyword, and should not be used for object names
           N'ORDER BY [Order];'; --ORDER is a reserved keyword, and should not be used for object names

DECLARE @Ticker varchar(10) = 'X',
        @ClientCode varchar(10) = 'Z';

--Print @SQL; -- Your best friend
EXEC sys.sp_executesql @SQL, N'@Ticker varchar(10), @ClientCode varchar(10)', @Ticker, @ClientCode;

db<>fiddle

如果您更改为@Col 变量赋值的查询,如下所示,您的查询将起作用。要定义 table 列,它们应该相同。没有 table 可以有两个同名的列。那是代码给出的错误。但我不确定您的预期输出。当我看到过滤数据的 where 子句时,我希望您的答案只有一行。

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

select @cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(FieldName) 
                from DynamicForm                    -              
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')


  set @query = N'SELECT ' + @cols + N' from 
         (
            select  value, FieldName
            from DynamicForm WHERE Ticker=''X'' AND ClientCode=''Z''
        ) x
        pivot 
        (
            max(value)
            for FieldName in (' + @cols + N')
        ) p '

   exec sp_executesql @query;