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