将列的值分隔为不同的列作为相同的查询

时间:2012-11-30 11:00:28

标签: sql

我有一个表UTENSILS,其中包含3列:

CLASS_NAME   RANGE    COUNT
---------------------------
pens         0-0.5     200
pencil       0-0.5      50
pens         0.5-1.0   300 
pencil       0.5-1.0    40
pens         1.0-1.5   150
pencil       1.0-1.5    45 

我想要一个显示上表结果的查询,如下所示:

RANGE      Pens        Pencils
------------------------------
0-0.5       200          50
0.5-1.0     300          40
1.0-1.5     150          45

有关于此的任何想法?提前谢谢!

3 个答案:

答案 0 :(得分:3)

您要做的事情被称为PIVOT。这是将数据从行转换为列的时间。某些数据库具有PIVOT功能,您可以利用它,但未指定哪个RDBMS。

如果您没有PIVOT功能,则可以使用聚合函数和CASE语句复制功能:

select `range`,
  sum(case when class_name = 'pens' then `count` end) pens,
  sum(case when class_name = 'pencil' then `count` end) pencils
from yourtable
group by `range`

请参阅SQL Fiddle with Demo

注意:如果SQL Server在rangecount附近使用方括号,则反引号适用于MySQL。这些用于转义保留字。

如果您正在使用具有PIVOT功能的RDBMS,那么您可以使用以下内容:

select *
from
(
  select class_name, [range], [count]
  from yourtable
) src
pivot
(
  sum([count])
  for class_name in ([pens], [pencil])
) piv

请参阅SQL Fiddle with Demo

两者都会产生相同的结果:

|   RANGE | PENS | PENCIL |
---------------------------
|   0-0.5 |  200 |     50 |
| 0.5-1.0 |  300 |     40 |
| 1.0-1.5 |  150 |     45 |

如果你有class_name的已知数量的值,那么上面的效果会很好,如果你没有,那么根据你的RDBMS,有很多方法可以生成这个查询的动态版本。

在SQL Server中,动态版本与此类似:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(CLASS_NAME) 
                    from yourtable
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT [range], ' + @cols + ' from 
             (
                select CLASS_NAME, [RANGE], [COUNT]
                from yourtable
            ) x
            pivot 
            (
                sum([COUNT])
                for CLASS_NAME in (' + @cols + ')
            ) p '

execute(@query)

请参阅SQL Fiddle with Demo

答案 1 :(得分:0)

此透视查询可用于所有主要DBMS。诀窍是将错误的列名称'range'和'count'引用属性。

SQL Server(下面)使用[],MySQL使用反引号(`),Oracle使用双引号,SQLite可以使用前面的任何一个。

  select [range],
         sum(case when class_name='pens' then [count] else 0 end) Pens,
         sum(case when class_name='pencil' then [count] else 0 end) Pencils
    from tbl
group by [range]
order by [range];

答案 2 :(得分:0)

这是枢轴的动态版本:

IF OBJECT_ID('tempdb..#T') IS NOT NULL
    DROP TABLE #T

CREATE TABLE #T(
    [CLASS_NAME] VARCHAR(20) NOT NULL
    , [range] VARCHAR(10) NOT NULL
    , [count] INT NOT NULL
)

INSERT INTO #T([CLASS_NAME], [range], [count]) VALUES ('pens', '0-0.5', '200')
INSERT INTO #T([CLASS_NAME], [range], [count]) VALUES ('pencil', '0-0.5', '50')
INSERT INTO #T([CLASS_NAME], [range], [count]) VALUES ('pens', '0.5-1.0', '300')
INSERT INTO #T([CLASS_NAME], [range], [count]) VALUES ('pencil', '0.5-1.0', '40')
INSERT INTO #T([CLASS_NAME], [range], [count]) VALUES ('pens', '1.0-1.5', '150')
INSERT INTO #T([CLASS_NAME], [range], [count]) VALUES ('pencil', '1.0-1.5', '45')

DECLARE @PivotColumnHeaders VARCHAR(MAX)

SELECT  @PivotColumnHeaders = STUFF((
        --SELECT DISTINCT TOP 100 PERCENT
        SELECT DISTINCT '],[' + [CLASS_NAME]
        FROM #T
        ORDER BY '],[' + [CLASS_NAME]
        FOR XML PATH('')
    ), 1, 2, '') + ']';

DECLARE @PivotTableSQL NVARCHAR(MAX)
SET @PivotTableSQL = N'
  SELECT *
  FROM (
    SELECT *
    FROM #T
  ) AS PivotData
  PIVOT (
    SUM([count])
    FOR [CLASS_NAME] IN (
      ' + @PivotColumnHeaders + '
    )
  ) AS PivotTable
'

EXECUTE(@PivotTableSQL)