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 BY
在 FieldName, id,Ticker,ClientCode
上。因此,您告诉 RDBMS 您想要为这些列的 every distinct 组排一行,很明显,这将导致相同的多行FieldName
.
的值
很可能 GROUP BY
和 ORDER 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;
如果您更改为@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;
我有一个 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 BY
在 FieldName, id,Ticker,ClientCode
上。因此,您告诉 RDBMS 您想要为这些列的 every distinct 组排一行,很明显,这将导致相同的多行FieldName
.
很可能 GROUP BY
和 ORDER 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;
如果您更改为@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;