在SQL select查询和连接表中动态创建列

时间:2013-06-17 07:28:35

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

我有2张桌子。它们如下

表:等级

GradeID | Grade 
----------------- 
1       | Chopsaw  
2       | Classic
3       | Chieve

表:模塑量

Batch ID | Grade | Moulded | Date 
-------------------------------------
1        | 1     | 150     | 21st May
2        | 1     | 150     | 22nd May
3        | 2     | 150     | 21st May
4        | 2     | 150     | 21st May
5        | 2     | 150     | 22nd May

我应该像以下

那样得到输出
Date       | Moulded  | Chopsaw | Classic | Cieve   
--------------------------------------------------
21st May   | 450      | 150     | 300     | 0    
22nd May   | 300      | 150     | 150     | 0

我正在使用MSSQL 2008,我使用Crystal报表来显示相同​​的内容。

2 个答案:

答案 0 :(得分:3)

如果事先知道成绩数,那么您可以使用静态查询来完成。

SELECT date,
       SUM(moulded) moulded,
       SUM(CASE WHEN grade = 1 THEN moulded ELSE 0 END) Chopsaw,
       SUM(CASE WHEN grade = 2 THEN moulded ELSE 0 END) Classic,
       SUM(CASE WHEN grade = 3 THEN moulded ELSE 0 END) Chieve
  FROM moulded_quantity 
 GROUP BY date

此查询不是特定于供应商的,因此它应该适用于任何主要的RDBMS。

现在,如果成绩数量未知或者您希望它能够工作,即使您对grade表进行了更改(不更改查询本身),也可以使用动态查询。但动态SQL是特定于供应商的。以下是如何在MySql中执行此操作的示例

SELECT CONCAT (
        'SELECT date, SUM(moulded) moulded,',
        GROUP_CONCAT(DISTINCT
        CONCAT('SUM(CASE WHEN grade = ',gradeid,
               ' THEN moulded ELSE 0 END) ', grade)),
        ' FROM moulded_quantity GROUP BY date') INTO @sql
FROM grade;

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

输出(在两种情况下):

|     DATE | MOULDED | CHOPSAW | CLASSIC | CHIEVE |
---------------------------------------------------
| 21st May |     450 |     150 |     300 |      0 |
| 22nd May |     300 |     150 |     150 |      0 |

以下是 SQLFiddle 演示(针对这两种方法)。

UPDATE 在Sql Server中,您可以使用STUFFPIVOT通过动态sql生成预期结果

DECLARE @colx NVARCHAR(MAX), @colp NVARCHAR(MAX), @sql NVARCHAR(MAX)

SET @colx = STUFF((SELECT ', ISNULL(' + QUOTENAME(Grade) + ',0) ' + QUOTENAME(Grade)
            FROM grade
            ORDER BY GradeID
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'),1,1,'')

SET @colp = STUFF((SELECT DISTINCT ',' + QUOTENAME(Grade)
            FROM grade
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'),1,1,'')

SET @sql = 'SELECT date, total moulded, ' + @colx +   
           '  FROM 
            (
              SELECT date, g.grade gradename, moulded,
                     SUM(moulded) OVER (PARTITION BY date) total
                FROM moulded_quantity q JOIN grade g
                  ON q.grade = g.gradeid
            ) x
            PIVOT 
            (
               SUM(moulded) FOR gradename IN (' + @colp + ')
            ) p
            ORDER BY date'

EXECUTE(@sql)

输出与MySql案例相同。

这是 SQLFiddle 演示。

答案 1 :(得分:0)

我建议你先问问题研究,因为这是非常常见的问题。

<强>更新

DECLARE @COLUMNS varchar(max) 
SELECT @COLUMNS = COALESCE(@COLUMNS+'],[' ,'') +  CAST(Grade as varchar)
FROM Grade
GROUP BY Grade


SET @COLUMNS = '[' + @COLUMNS + ']'


DECLARE @COLUMNS_WITH_NULL varchar(max) 
SELECT @COLUMNS_WITH_NULL  = COALESCE(@COLUMNS_WITH_NULL+',ISNULL([' ,'ISNULL([') +  CAST(Grade as varchar) + '], 0) AS ' +  CAST(Grade as varchar) 
FROM Grade
GROUP BY Grade

DECLARE @COLUMNS_SUMS varchar(max) 
SELECT @COLUMNS_SUMS  = COALESCE(@COLUMNS_SUMS+' + ISNULL([' ,'ISNULL([') +  CAST(Grade as varchar) + '], 0) ' 
FROM Grade
GROUP BY Grade


SET @COLUMNS_SUMS = '(' + @COLUMNS_SUMS + ') as Moulded'
PRINT @COLUMNS_SUMS


EXECUTE (
'
SELECT 
    Date, ' + @COLUMNS_SUMS + ', ' +  @COLUMNS_WITH_NULL + '
FROM (

SELECT 
    m.Moulded,
    m.date AS Date,
    g.Grade
FROM Grade g
INNER JOIN [Moulded Quantity] m
ON m.GRADE = g.GradeID
) up
PIVOT (SUM(Moulded) FOR  Grade IN ('+ @COLUMNS +')) AS pvt')