从表中存储的列生成报告

时间:2017-01-10 21:26:11

标签: 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       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

2 个答案:

答案 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]