Sql server 2012 从表中存储的列生成报告
我想根据数据和类别表生成以下报告: 类别表保存类别和指向数据表上字段的指针:Sql server 2012 从表中存储的列生成报告,sql-server-2012,Sql Server 2012,我想根据数据和类别表生成以下报告: 类别表保存类别和指向数据表上字段的指针: CAT FIELD PRINTER P1 CHAIR P3 TABLE P2 数据表包含数据和物理字段: ITEM_ID P1 P2 P3 P4 1 A B C D 2 X Y Z A 3 N M O P 报告应该是这样的: ITEM_ID CAT 1 PRINTER_A 1
CAT FIELD
PRINTER P1
CHAIR P3
TABLE P2
数据表包含数据和物理字段:
ITEM_ID P1 P2 P3 P4
1 A B C D
2 X Y Z A
3 N M O P
报告应该是这样的:
ITEM_ID CAT
1 PRINTER_A
1 CHAIR_C
1 TABLE_B
2 PRINTER_X
2 CHAIR_Z
2 TABLE_Y
3 PRINTER_N
3 CHAIR_O
3 TABLE_M
对于解决方案,我可以获取数据表中的所有项
然后循环每个类别并进行插入,
但是,数据表中有数百万项,类别表中有20多个项,这将有很差的性能
SELECT
D.ITEM_ID,
RTRIM(Cast(C.CAT as nVarChar)) + '_' +
CASE C.FIELD
WHEN 'P1'
THEN d.P1
WHEN 'P2'
THEN d.P2
WHEN 'P3'
THEN d.P3
WHEN 'P4'
THEN d.P4
ELSE NULL
END as Cat
FROM Data D
CROSS JOIN Category C
ORDER BY ITEM_ID, FIELD
你知道如何有效地生成这个吗
资料来源:
CREATE TABLE [dbo].[CAT_REPORT](
[ITEM_ID] [nchar](100) NULL,
[CAT] [nchar](100) NULL
)
GO
CREATE TABLE [dbo].[DATA](
[ITEM_ID] [nchar](10) NULL,
[P1] [nchar](50) NULL,
[P2] [nchar](50) NULL,
[P3] [nchar](50) NULL,
[P4] [nchar](50) NULL
)
CREATE TABLE [dbo].[CATEGORY](
[CAT] [nchar](10) NULL,
[FIELD] [nchar](10) NULL
)
INSERT [dbo].[CATEGORY] ([CAT], [FIELD]) VALUES ('PRINTER', 'P1')
GO
INSERT .[CATEGORY] ([CAT], [FIELD]) VALUES ('CHAIR', 'P3')
GO
INSERT .[CATEGORY] ([CAT], [FIELD]) VALUES ('TABLE', 'P2')
GO
INSERT .[DATA] ([ITEM_ID], [P1], [P2], [P3], [P4]) VALUES ('1', 'A', 'B', 'C', 'D')
GO
INSERT .[DATA] ([ITEM_ID], [P1], [P2], [P3], [P4]) VALUES ('2', 'X', 'Y', 'Z', 'A')
GO
INSERT .[DATA] ([ITEM_ID], [P1], [P2], [P3], [P4]) VALUES ('3', 'N', 'M', 'O', 'P')
GO
这是我到目前为止得到的存储:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
DROP procedure fill_category_report_table
go
CREATE PROCEDURE fill_category_report_table
AS
BEGIN
SET NOCOUNT ON;
DECLARE @CatName nvarchar(20),@CatField nvarchar(20),@ItemId nvarchar(20),@CatNameOut nvarchar(20),@out_var nvarchar(20)
DECLARE @sql nvarchar(100)
DECLARE DATA_CUR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT ITEM_ID from [DATA]
OPEN DATA_CUR
FETCH NEXT FROM DATA_CUR INTO @ItemId
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @ItemId
DECLARE CAT_CUR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT CAT,FIELD from CATEGORY
OPEN CAT_CUR
FETCH NEXT FROM CAT_CUR INTO @CatName,@CatField
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @CatName
SET @sql = N'SELECT @CatNameOut=@CatName + ''_'' + ' + @CatField + ' FROM [DATA] where ITEM_ID=' +@ItemId
EXECUTE sp_executesql @sql,N'@CatName varchar(100), @CatNameOut varchar(100) OUTPUT',@CatNameOut = @CatNameOut output,@CatName=@CatName;
INSERT INTO CAT_REPORT ([ITEM_ID],[CAT]) VALUES (@ItemId ,@CatNameOut)
FETCH NEXT FROM CAT_CUR INTO @CatName,@CatField
END
CLOSE CAT_CUR
DEALLOCATE CAT_CUR
FETCH NEXT FROM DATA_CUR INTO @ItemId
END
CLOSE DATA_CUR
DEALLOCATE DATA_CUR
END
GO
根据您的样本数据,这将执行查询,而无需使用光标-这将彻底破坏您的性能
SELECT
D.ITEM_ID,
RTRIM(Cast(C.CAT as nVarChar)) + '_' +
CASE C.FIELD
WHEN 'P1'
THEN d.P1
WHEN 'P2'
THEN d.P2
WHEN 'P3'
THEN d.P3
WHEN 'P4'
THEN d.P4
ELSE NULL
END as Cat
FROM Data D
CROSS JOIN Category C
ORDER BY ITEM_ID, FIELD
顺便说一下,如果没有很好的理由,您真的不应该将数据存储在char/nchar字段中。这些字段使用全部数据空间,即使它们只存储一个字符。Varchar/nVarchar是一种更为紧凑的数据存储方式,除非您绝对需要存储在字段中的每个值具有相同的长度。您肯定希望取消RBAR(一行一行)光标 您的代码正在执行数据的取消PIVOT,在SQL Server中,取消PIVOT功能不是很好,取消PIVOT数据的更好方法是使用值交叉应用 根据评论,听起来您不需要动态查询。在SQL中,动态查询往往用于处理可能需要动态访问不同表或列的查询。所有似乎发生变化的是Category表中的记录,但它们总是在相关的地方引用P1-P4列
--Query 2
--Dynamically build Field list from CATEGORY table.
DECLARE @Fields NVARCHAR(200) = (
SELECT TOP 1
STUFF((SELECT ',(''' + RTRIM(FIELD) + ''',D.' + RTRIM(FIELD) + ')' FROM [dbo].[CATEGORY] FOR XML PATH ('')),1,1,'') FIELD
FROM [dbo].[CATEGORY]
WHERE
--Ensure the column name exists in DATA
Field IN (SELECT name from sys.columns WHERE object_id = object_id('Data'))
)
--Build query to look at Fields - use OPITON(RECOMPILE) to prevent Parameter Sniffing
DECLARE @QRY NVARCHAR(2000) = '
INSERT INTO dbo.[CAT_REPORT]
SELECT
D.ITEM_ID
,RTRIM(C.CAT) + ''_'' + cl.Val AS [CAT]
FROM
[dbo].[DATA] D
CROSS APPLY (VALUES ' + @Fields + ') AS cl(Field,Val)
INNER JOIN [dbo].[CATEGORY] C
ON cl.Field = C.FIELD
OPTION (RECOMPILE)'
--Run Query with Dynamically identified Fields
EXEC (@QRY)
SELECT * FROM dbo.[CAT_REPORT]
您的表结构似乎是静态的-只有数据发生了变化-如果是这样,查询1就可以了
--Query 1
INSERT INTO dbo.[CAT_REPORT]
SELECT
D.ITEM_ID
,RTRIM(C.CAT) + '_' + cl.Val AS [CAT]
FROM
dbo.[DATA] D
CROSS APPLY (VALUES ('P1',D.P1),('P3',D.P3),('P2',D.P2)) AS cl(Field,Val)
INNER JOIN dbo.[CATEGORY] C
ON cl.Field = C.FIELD
如果数据表的结构可以更改,那么查询2将是相关的
--Query 2
--Dynamically build Field list from CATEGORY table.
DECLARE @Fields NVARCHAR(200) = (
SELECT TOP 1
STUFF((SELECT ',(''' + RTRIM(FIELD) + ''',D.' + RTRIM(FIELD) + ')' FROM [dbo].[CATEGORY] FOR XML PATH ('')),1,1,'') FIELD
FROM [dbo].[CATEGORY]
WHERE
--Ensure the column name exists in DATA
Field IN (SELECT name from sys.columns WHERE object_id = object_id('Data'))
)
--Build query to look at Fields - use OPITON(RECOMPILE) to prevent Parameter Sniffing
DECLARE @QRY NVARCHAR(2000) = '
INSERT INTO dbo.[CAT_REPORT]
SELECT
D.ITEM_ID
,RTRIM(C.CAT) + ''_'' + cl.Val AS [CAT]
FROM
[dbo].[DATA] D
CROSS APPLY (VALUES ' + @Fields + ') AS cl(Field,Val)
INNER JOIN [dbo].[CATEGORY] C
ON cl.Field = C.FIELD
OPTION (RECOMPILE)'
--Run Query with Dynamically identified Fields
EXEC (@QRY)
SELECT * FROM dbo.[CAT_REPORT]
你能把你解决这个问题的尝试写进去吗?谢谢你的帮助。添加了我编写的存储过程,它的性能很差。您能提供动态表的不同之处的示例吗?不同之处在于分类表中的记录很少或更少。。。例如,添加另一个类别:壁橱P5。因此,如果有一个新行壁橱P5,那么您会将其从结果中排除,因为数据中没有P5列?谢谢您,问题是我无法硬编码类别表,原因是该表是动态的,但您不能将临时表编码到此查询中吗?我认为这是最好的方法(假设它有效)。性能将很好(除非您有数百万个熔炉项目…不太可能)我无法使用查询1->这是硬编码的:('P1',D.P1),('P3',D.P3),('P2',D.P2))让我测试查询2