【问题标题】:Dynamic SELECT statement, generate columns based on present and future values动态 SELECT 语句,根据当前值和未来值生成列
【发布时间】:2014-06-11 23:26:40
【问题描述】:

目前正在 SQL Server 2008 中构建 SELECT 语句,但希望使此 SELECT 语句动态化,因此可以根据表中的值定义列。我听说过数据透视表和游标,但在我目前的水平上似乎有点难以理解,这里是代码;

DECLARE @date DATE = null

IF @date is null
set @ date = GETDATE() as DATE

SELECT
    Name,
    value1,
    value2,
    value3,
    value4
FROM ref_Table a
FULL OUTER JOIN (
    SELECT
       PK_ID ID,
       sum(case when FK_ContainerType_ID = 1 then 1 else null) Box,
       sum(case when FK_ContainerType_ID = 2 then 1 else null) Pallet,
       sum(case when FK_ContainerType_ID = 3 then 1 else null) Bag,
       sum(case when FK_ContainerType_ID = 4 then 1 else null) Drum
    from 
       Packages  
    WHERE 
       @date between PackageStart AND PackageEnd
    group by PK_ID ) b on a.Name = b.ID
where 
    Group = 0

以下内容对我很有用,但是 PK_Type_ID 和列的名称(PackageNameX,..)是硬编码的,我需要是动态的,它可以根据 Package 表中的当前值或未来值构建自己.

任何关于正确方向的帮助或指导将不胜感激......,

根据要求

ref_Table(PK_ID,名称)

1, John
2, Mary
3, Albert
4, Jane

Packages(PK_ID、FK_ref_Table_ID、FK_ContainerType_ID、PackageStartDate、PackageEndDate)

1 , 1, 4, 1JAN2014, 30JAN2014
2 , 2, 3, 1JAN2014, 30JAN2014
3 , 3, 2, 1JAN2014, 30JAN2014
4 , 4, 1, 1JAN2014, 30JAN2014

ContainerType(PK_ID,类型)

1, Box
2, Pallet
3, Bag
4, Drum

结果应该是这样的;

Name     Box     Pallet    Bag    Drum
---------------------------------------
John                                1
Mary                        1
Albert              1
Jane       1

我说的以下代码效果很好,问题是Container 表会增长,我需要复制相同的报告而不对列进行硬编码。

【问题讨论】:

  • 您能否提供一个包含一些示例数据和预期输出示例的架构?

标签: sql sql-server-2008 cursor pivot-table


【解决方案1】:

您需要构建的内容称为动态枢轴。如果您搜索该术语,Stack 上有很多很好的参考资料。

这是您的方案的解决方案:

IF OBJECT_ID('tempdb..##ref_Table') IS NOT NULL
    DROP TABLE ##ref_Table
IF OBJECT_ID('tempdb..##Packages') IS NOT NULL
    DROP TABLE ##Packages
IF OBJECT_ID('tempdb..##ContainerType') IS NOT NULL
    DROP TABLE ##ContainerType

SET NOCOUNT ON

CREATE TABLE ##ref_Table (PK_ID INT, NAME NVARCHAR(50))
CREATE TABLE ##Packages (PK_ID INT, FK_ref_Table_ID INT, FK_ContainerType_ID INT, PackageStartDate DATE, PackageEndDate DATE)
CREATE TABLE ##ContainerType (PK_ID INT, [Type] NVARCHAR(50))

INSERT INTO ##ref_Table (PK_ID,NAME)
SELECT 1,'John' UNION
SELECT 2,'Mary' UNION
SELECT 3,'Albert' UNION
SELECT 4,'Jane'

INSERT INTO ##Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
SELECT 1,1,4,'2014-01-01','2014-01-30' UNION
SELECT 2,2,3,'2014-01-01','2014-01-30' UNION
SELECT 3,3,2,'2014-01-01','2014-01-30' UNION
SELECT 4,4,1,'2014-01-01','2014-01-30' 

INSERT INTO ##ContainerType (PK_ID, [Type])
SELECT 1,'Box' UNION
SELECT 2,'Pallet' UNION
SELECT 3,'Bag' UNION
SELECT 4,'Drum' 

DECLARE @DATE DATE, @PARAMDEF NVARCHAR(MAX), @COLS NVARCHAR(MAX), @SQL NVARCHAR(MAX)
SET @DATE = '2014-01-15'

SET @COLS = STUFF((SELECT DISTINCT  ',' + QUOTENAME(T.[Type])
            FROM ##ContainerType T
            FOR XML PATH, TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')

SET @SQL = 'SELECT [Name], ' + @COLS + ' 
            FROM (SELECT [Name], [Type], 1 AS Value
                  FROM ##ref_Table R
                  JOIN ##Packages P ON R.PK_ID = P.FK_ref_Table_ID
                  JOIN ##ContainerType T ON P.FK_ContainerType_ID = T.PK_ID
                  WHERE @DATE BETWEEN P.PackageStartDate AND P.PackageEndDate) X
            PIVOT (COUNT(Value) FOR [Type] IN (' + @COLS + ')) P
            '
PRINT @COLS
PRINT @SQL
SET @PARAMDEF = '@DATE DATE'
EXEC SP_EXECUTESQL @SQL, @PARAMDEF, @DATE=@DATE

输出:

Name    Bag     Box     Drum    Pallet
Albert  0       0       0       1
Jane    0       1       0       0
John    0       0       1       0
Mary    1       0       0       0

【讨论】:

  • 注意:如果您希望看到 NULL 而不是 0 来表示缺失数据,请将动态 SQL 字符串中的 COUNT(Value) 更改为 SUM(Value)
【解决方案2】:

静态查询:

SELECT [Name],[Box],[Pallet],[Bag],[Drum] FROM
(
    SELECT *
    FROM
    (
        SELECT  rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
        FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
        INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
     ) AS SourceTable
    PIVOT
    (
           COUNT(PKID )
           FOR [Type]
           IN ( [Box],[Pallet],[Bag],[Drum])
    ) AS PivotTable
) AS Main
ORDER BY RFID



动态查询:

DECLARE @columnList nvarchar (MAX)
DECLARE @pivotsql nvarchar (MAX)

SELECT @columnList =   STUFF(
             (
             SELECT ',' + '[' + [Type] + ']'
           FROM ContanerType
           FOR XML PATH( '')
           )
        ,1, 1,'' )


SET @pivotsql =
N'SELECT [Name],' + @columnList + ' FROM
(
    SELECT *
    FROM
    (
        SELECT  rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
        FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
        INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
     ) AS SourceTable
    PIVOT
    (
           COUNT(PKID )
           FOR [Type]
           IN ( ' + @columnList + ')
    ) AS PivotTable
) AS Main
ORDER BY RFID;'

EXEC sp_executesql @pivotsql


按照下面的教程将帮助您了解 PIVOT 功能:

我们编写 sql 查询是为了从数据库表中获取不同的结果集,例如完整、部分、计算、分组、排序等。然而,有时我们有要求我们必须轮换我们的桌子。听起来很混乱?

让我们保持简单,并考虑以下两个屏幕截图。

SQL 表:

预期结果:

哇,看起来工作量很大!那是复杂的sql,临时表,循环,聚合……,等等等等的组合

别担心,让我们保持简单,愚蠢的(KISS)。

MS SQL Server 2005 及更高版本有一个称为 PIVOT 的函数。它使用起来非常简单且功能强大。借助此功能,我们将能够轮换 sql 表和结果集。

实现它的简单步骤:

  1. 确定将成为所需结果集一部分的所有列。
  2. 找到我们将应用聚合的列(sum、ave、max、min 等)
  3. 确定哪些值将成为列标题的列。
  4. 用逗号分隔并用方括号括起来指定步骤3中提到的列值。

所以,如果我们现在按照上面的四个步骤,从上面的销售表中提取信息,它将如下:

  1. 年、月、销售额
  2. 销售额
  3. [一月]、[二月]、[三月] ....等

如果到目前为止所有上述步骤对您来说都有意义,我们就快到了。

现在我们拥有了我们需要的所有信息。我们现在要做的就是在下面的模板中填写所需的信息。 模板: 我们的 SQL 查询应该如下所示:

选择 *

FROM
(
    SELECT SalesYear, SalesMonth,Amount
    FROM Sales
 ) AS SourceTable
PIVOT
(
       SUM(Amount )
       FOR SalesMonth
       IN ( [Jan],[Feb] ,[Mar],
            [Apr],[May],[Jun] ,[Jul],
            [Aug],[Sep] ,[Oct],[Nov] ,[Dec])
) AS PivotTable;

在上面的查询中,我们对列名进行了硬编码。好吧,当您必须指定列数时,这并不好玩。

但是,有一个工作范围如下:

DECLARE @columnList nvarchar (MAX)
DECLARE @pivotsql nvarchar (MAX)

SELECT @columnList =   STUFF(
             (
               SELECT ',' + '[' + SalesMonth + ']'
               FROM Sales
               GROUP BY SalesMonth
               FOR XML PATH( '')
           )
        ,1, 1,'' )

SET @pivotsql =
       N'SELECT *
      FROM
      (
            SELECT SalesYear, SalesMonth,Amount
            FROM Sales
       ) AS SourceTable
      PIVOT
      (
           SUM(Amount )
           FOR SalesMonth
           IN ( ' + @columnList +' )
      ) AS PivotTable;'

EXEC sp_executesql @pivotsql

希望本教程对某处的人有所帮助。 享受编码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-27
    • 2011-02-15
    • 2011-08-22
    • 2020-08-18
    • 2015-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多