SQL表:我可以透过N行进行透视/连接吗?

时间:2012-02-08 12:39:28

标签: sql database datagrid pivot-table

首先,我是一个SQL noob,第二,如果有更好的解决方案可以在以后的代码中实现,我很乐意听到它。此外,数据当前存储在MySQL中,但最终必须移植到MSSQL,因此跨数据库解决方案最好(如果存在)。

现在,问题是,我的简化数据如下所示:

[STYLES]
ID   NAME
1    A Style
2    B Style
...
N    N Style

[EQUIPMENT]
ID   NAME
1    A Equipment
2    B Equipment
...
N    N Equipment

[AVAILABILITY]
STYLE  EQUIPMENT  TYPE
1      1          Standard
1      2          Optional
2      1          Optional
... #items will be missing and represent not available
2      2          Standard

现在我需要一个看起来像这样的表:

[DESIRED_VIEW]
EQUIPMENT_NAME   A_STYLE_TYPE  B_STYLE_TYPE ... N_STYLE_TYPE
A Equipment      Standard      Optional     ... NULL
B Equipment      Optional      NULL         ... Standard

我已经看到了许多简单的数据透视示例,它们都依赖于具有一定数量的列。有没有办法根据STYLES表中有多少行来设置具有可变列数的视图?

作为一个注释,我使用Visual Studio创建Data xsd并让它自动生成表填充方法,然后在WPF DataGrids中显示信息,这样能够直接绑定到具有正确数据的视图将是理想的

2 个答案:

答案 0 :(得分:2)

是的,但仅限于使用动态SQL(存储过程)时。 这是现实生活中的一个例子

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_RPT_Report_Translation]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[sp_RPT_Report_Translation]
GO







-- ===========================================================
-- Author:        [CENSORED]
-- Create date:   14.04.2011
-- Last modified: 17.01.2012
-- Description:   Übersetzung für Berichte
-- ===========================================================
-- Pre: Valid Report Name & datetime
-- Post: Translation for language in first row with rownames as defined 
--       for each item in T_RPT_Translations.RTR_ItemCaption
CREATE PROCEDURE [dbo].[sp_RPT_Report_Translation]
     @in_mandant varchar(3) 
    ,@in_sprache varchar(2) 
    ,@in_stichtag varchar(50) 
    ,@in_report_name nvarchar(1000) 
AS
BEGIN
    DECLARE 
     @strSQL NVARCHAR(MAX) 
    ,@strReportName NVARCHAR(1000) 
    ,@strPivotColumns NVARCHAR(MAX) 
    ,@stichtag DATETIME 


    -- Abrunden des Eingabedatums auf 00:00:00 Uhr 
    SET @stichtag    = CONVERT(DATETIME, @in_stichtag) 
    SET @stichtag    = CAST(FLOOR(CAST(@stichtag AS Float)) AS DateTime) 
    SET @in_stichtag = CONVERT(varchar(50), @stichtag) 

    SET NOCOUNT ON;

    SET @strReportName = REPLACE(@in_report_name, N'''', '''''') 

    -- http://geekswithblogs.net/baskibv/archive/2008/07/03/123567.aspx
    SELECT 
        @strPivotColumns = COALESCE(@strPivotColumns, '') + '[' + [RTR_ItemCaption] + '], ' 
    FROM T_RPT_Translations 
    WHERE (RTR_Status = 1) 
        AND (RTR_MDT_ID = @in_mandant) 
        AND 
        ( 
            (RTR_ReportName = @strReportName) 
            OR 
            (RTR_ReportName = 'PARA_ALL') 
        ) 
        --AND (RTR_ItemCaption != 'RPT_Title') 
        AND (RTR_ItemCaption IS NOT NULL) 
        AND 
        (
                (RTR_IsFlag != 1) 
                OR 
                (RTR_IsFlag IS NULL) 
        ) 
        AND (RTR_ItemCaption != '') 
    ORDER BY RTR_Sort



    SET @strPivotColumns = SUBSTRING(@strPivotColumns, 0, LEN(@strPivotColumns)) 
    SET @strPivotColumns = REPLACE(@strPivotColumns, N'''', '''''') 

    --PRINT @strPivotColumns

    SET @strSQL = '
        SELECT TOP(1) * FROM 
        ( 
            SELECT 
                 RTR_ItemCaption 
                --,RTR_Kurz_' + @in_sprache + ' 
                ,RTR_Lang_' + @in_sprache + ' 
            FROM T_RPT_Translations 
            WHERE (RTR_MDT_ID = ''' + @in_mandant+ ''') 
                AND
                ( 
                    (RTR_ReportName = ''' + @strReportName + ''') 
                    OR 
                    (RTR_ReportName = ''PARA_ALL'')
                )
                --AND (RTR_ItemCaption != ''RPT_Title'') 
                AND (RTR_Status = 1) 
                AND (RTR_ItemCaption IS NOT NULL)
                AND 
                (
                    (RTR_IsFlag != 1) 
                    OR 
                    (RTR_IsFlag IS NULL) 
                ) 
                AND (RTR_ItemCaption != '''')  

        ) AS SourceTable 
        PIVOT 
        ( 
            MAX(RTR_Lang_' + @in_sprache + ') 
            FOR RTR_ItemCaption IN 
            ( ' 
                + @strPivotColumns + 
          ' ) 

        ) AS PivotTable 

        --ORDER BY RPT_RM_SO_Bezeichnung, RPT_RM_GB_Bezeichnung, RPT_RM_NutzungGruppeCode 
    ' 

    DECLARE @ProzedurParameter nvarchar(max)
    SET @ProzedurParameter = '
    DECLARE @in_mandant varchar(3) 
    ,@in_sprache varchar(2) 
    ,@in_stichtag varchar(50) 
    ,@in_report_name nvarchar(1000) 
    ;

    SET @in_mandant = ''' + REPLACE(@in_mandant, '''', '''''') + '''; 
    SET @in_sprache = ''' + REPLACE(@in_sprache, '''', '''''') + '''; 
    SET @in_stichtag = ''' + REPLACE(@in_stichtag, '''', '''''') + '''; 
    SET @in_report_name = ''' + REPLACE(@in_report_name, '''', '''''') + '''; 
    '



    EXECUTE sp_RPT_DEBUG_LOG_ProzedurRun 
        'sp_RPT_Report_Translation'
        ,@ProzedurParameter
        ,@strSQL 
        ,'' --@ProzedurDetail
    ;


    --PRINT @strSQL
    EXECUTE (@strSQL)

END



GO

表格:

CREATE TABLE [dbo].[T_RPT_Translations](
    [RTR_UID] [uniqueidentifier] NULL,
    [RTR_ReportName] [nvarchar](1000) NULL,
    [RTR_MDT_ID] [int] NULL,
    [RTR_ItemCaption] [nvarchar](50) NULL,
    [RTR_Code] [int] NULL,
    [RTR_nCode] [nvarchar](100) NULL,
    [RTR_Kurz_DE] [nvarchar](20) NULL,
    [RTR_Kurz_FR] [nvarchar](20) NULL,
    [RTR_Kurz_IT] [nvarchar](20) NULL,
    [RTR_Kurz_EN] [nvarchar](20) NULL,
    [RTR_Lang_DE] [nvarchar](100) NULL,
    [RTR_Lang_FR] [nvarchar](100) NULL,
    [RTR_Lang_IT] [nvarchar](100) NULL,
    [RTR_Lang_EN] [nvarchar](100) NULL,
    [RTR_Img_DE] [varchar](max) NULL,
    [RTR_Img_FR] [varchar](max) NULL,
    [RTR_Img_IT] [varchar](max) NULL,
    [RTR_Img_EN] [varchar](max) NULL,
    [RTR_Img_Width] [int] NULL,
    [RTR_Img_Height] [int] NULL,
    [RTR_Img_PaddingLeft] [float] NULL,
    [RTR_Img_PaddingRight] [float] NULL,
    [RTR_Img_PaddingTop] [float] NULL,
    [RTR_Img_PaddingBottom] [float] NULL,
    [RTR_Img_Hide] [bit] NULL,
    [RTR_IsLogo] [bit] NULL,
    [RTR_IsFlag] [bit] NULL,
    [RTR_Sort] [int] NULL,
    [RTR_Status] [int] NULL,
    [RTR_DatumVon] [datetime] NULL,
    [RTR_DatumBis] [datetime] NULL
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_UID]  DEFAULT (newid()) FOR [RTR_UID]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_ReportName]  DEFAULT (N'InsertError') FOR [RTR_ReportName]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_MDT_ID]  DEFAULT ((0)) FOR [RTR_MDT_ID]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_ItemCaption]  DEFAULT (N'InsertError') FOR [RTR_ItemCaption]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_IsLogo]  DEFAULT ((0)) FOR [RTR_IsLogo]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_IsFlag]  DEFAULT ((0)) FOR [RTR_IsFlag]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_Sort]  DEFAULT ((0)) FOR [RTR_Sort]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_Status]  DEFAULT ((1)) FOR [RTR_Status]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_DatumVon]  DEFAULT ('17530101') FOR [RTR_DatumVon]
GO

ALTER TABLE [dbo].[T_RPT_Translations] ADD  CONSTRAINT [DF_T_RPT_Translations_RTR_DatumBis]  DEFAULT ('99991231') FOR [RTR_DatumBis]
GO

如果您不理解德语参数:

@in_sprache: in_language
@in_stichtag: in_ReportingDate
DatumVon: DateFrom
DatumBis: DateTo
rest is clear

RTR_Img_XX是一个基于64位编码的图像,其中mime在RTR_nCode中定义,万一你想知道。

答案 1 :(得分:0)

否 - 无法编写具有动态列数的查询。

您的选择(按优先顺序排列):

  1. 编写一个包含“很多”列的查询,其中一些可能是空白的(易于编写和使用,但在枢轴宽度上会有一个最大硬度)
  2. 编写为您编写动态查询的应用程序代码(不太糟糕:可移植)
  3. 编写存储过程来创建SQL(糟糕的选择:在PL / SQL中编写逻辑代码很痛苦,完全不可移植)
  4. 如果您对最大数量的样式感到满意(它可以是任意大的,但是有限的),您的数据透视代码将如下所示:

    select
      e.name as equipment_name,
      a1.type as a_style_type,
      a2.type as b_style_type,
      a3.type as c_style_type,
      ...
      an.type as n_style_type
    from equipment e
    left join availability a1 on a1.equipment = e.id and a1.style = 1
    left join availability a2 on a2.equipment = e.id and a2.style = 2
    left join availability a3 on a3.equipment = e.id and a3.style = 3
    ...
    left join availability an on an.equipment = e.id and an.style = 999;
    

    如果给定样式没有可用性类型,您将获得NULL

    此外,您需要知道样式ID,并且它们需要在所有environemtns上相同。如果不是这种情况(例如测试/产品环境不同),您可以将a1.style = 1更改为a1.style = (select id from style where name = 'A Style')等 - 它仍然可以正常运行。