动态SELECT语句,根据当前和未来值生成列

时间:2014-06-11 23:26:41

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

目前在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,..)是硬编码的,我需要是动态的,它可以根据{{1}中的当前或期货值构建自己} table。

非常感谢任何有关正确方向的帮助或指导......

根据要求

Package(PK_ID,姓名)

ref_Table

1, John 2, Mary 3, Albert 4, Jane (PK_ID,FK_ref_Table_ID,FK_ContainerType_ID,PackageStartDate,PackageEndDate)

Packages

1 , 1, 4, 1JAN2014, 30JAN2014 2 , 2, 3, 1JAN2014, 30JAN2014 3 , 3, 2, 1JAN2014, 30JAN2014 4 , 4, 1, 1JAN2014, 30JAN2014 (PK_ID,类型)

ContainerType

,结果应如下所示;

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

我说的以下代码效果很好,问题是Name Box Pallet Bag Drum --------------------------------------- John 1 Mary 1 Albert 1 Jane 1 表会增长,我需要复制相同的报告而不对列进行硬编码。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:-1)

静态查询:

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.     
  3. 找到我们将应用聚合的列(sum,ave,max,min等)
  4.     
  5. 确定哪些值将成为列标题的列。
  6.     
  7. 使用逗号分隔并用方括号括起来指定 step3 中提到的列值。
  8. 因此,如果我们现在按照以上四个步骤从上面的销售表中提取信息,它将如下所示:

          
    1. 年,月,SalesAmount
    2.     
    3. SalesAmount两个
    4.     
    5.     
    6. [Jan],[Feb],[Mar] .... etc
    7. 如果上述所有步骤到目前为止对我们有意义的话,我们几乎就在那里。

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

      SELECT *

      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
      

      希望本教程能够帮助某个人。 享受编码。