我想根据数据和类别表生成以下报告:
类别表保存数据表中字段的类别和指针:
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
对于解决方案,我可以获取DATA表中的所有ITEMS 然后循环每个CATEGORY并进行插入, 但是DATA表中有数百万项,类别表中有20多项,性能会很差。
知道如何有效地生成这个吗?
来源:
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
答案 0 :(得分:2)
根据您的示例数据,这会执行查询而无需使用光标 - 这将 绝对RUIN 您的表现。
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是一种更紧凑的存储数据的方式,除非你绝对需要存储在字段中的每个值都是相同的长度。
答案 1 :(得分:1)
你肯定想要一个BLAR(Row By Agonizing Row)光标的空白。
你的代码正在执行一些数据,在SQL Server中,UNPIVOT函数不是很好,更好的方法是对数据进行非文件删除是使用CROSS APPLY和VALUES
基于评论,听起来您不需要动态查询。在SQL中,动态查询倾向于用于处理可能需要动态访问不同表或列的查询。所有似乎都改变的是类别表中的记录,但它们总是指相关的P1 - P4列。
您的表结构似乎是静态的 - 只是数据发生了变化 - 如果是这样,那么查询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]