计算多列的值数

时间:2014-12-10 13:29:51

标签: sql sql-server count pivot unpivot

我有一个包含11列的表格。第一列包括类别名称。剩余的10列具有白色,绿色,大,损坏等值,这些值可以及时更改。

我需要一个SQL查询来查找每个值(10列)中每个值的数量。

表1:

+------------+------------+
|  ID        | decription |
+------------+------------+
| 1          | white      |
| 2          | green      |
| 3          | big        |
| 4          | damaged    |
+------------+------------+

表2:

+------------+-----------+-----------+-----------+
|  CATEGORY  | SECTION 1 | SECTION 2 | SECTION 3 |
+------------+-----------+-----------+-----------+
| Category 1 | white     | green     | big       |
| Category 2 | big       | damaged   | white     |
| Category 1 | white     | green     | big       |
| Category 3 | big       | damaged   | white     |
+------------+-----------+-----------+-----------+

期望的结果:

+------------+-------+-------+-----+---------+
|  CATEGORY  | White | Green | Big | Damaged |
+------------+-------+-------+-----+---------+
| Category 1 |    20 |    10 |   9 |      50 |
| Category 2 |    25 |    21 |  15 |       5 |
+------------+-------+-------+-----+---------+

是否可以像查询一样动态地执行此操作?

它在Visual Studio报告中的MS sql上

由于

2 个答案:

答案 0 :(得分:4)

你对设计和所需的结果感到有点混乱。问题是你的表是非规范化的,然后你想要的最终结果也是非规范化的。您可以通过取消忽略Section列,然后旋转这些列的值来获得最终结果。你需要动态地进一步添加到混乱中。

首先,我建议你重新考虑你的表结构,因为这太难以维护了。

与此同时,在您考虑编写动态版本以获得结果之前,您必须通过静态或硬编码查询来获得逻辑正确。现在,您没有说明您正在使用的SQL Server版本,但您首先需要取消忽略Section列。您可以使用UNPIVOT功能或CROSS APPLY。您的查询将从类似于以下内容开始:

select 
  category,
  value
from yourtable 
unpivot
(
  value for cols in (Section1,Section2,Section3)
) u

SQL Fiddle with Demo。这会将您的数据转换为以下格式:

|   CATEGORY |   VALUE |
|------------|---------|
| Category 1 |   white |
| Category 1 |   green |
| Category 1 |     big |
| Category 2 |     big |
| Category 2 | damaged |
| Category 2 |   white |

现在您有多个Category行 - 每个值对应以前在Section列中的每个值。由于您需要Category中每个单词的总计数,现在可以应用数据透视功能:

select 
  category,
  white, green, big, damaged
from
(
  select 
    category,
    value
  from yourtable 
  unpivot
  (
    value for cols in (Section1,Section2,Section3)
  ) u
) un
pivot
(
  count(value)
  for value in (white, green, big, damaged)
) p;

SQL Fiddle with Demo。这将为您提供所需的结果,但现在您需要动态完成此操作。您必须使用动态SQL,它将创建一个将执行的SQL字符串,为您提供最终结果。

如果UNPIVOT的列数有限,那么您将在字符串中创建新列值的列表,然后执行它类似于:

DECLARE @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX);


select @colsPivot 
        = STUFF((SELECT ',' + quotename(SectionValue)
                 from yourtable
                 cross apply
                 (
                   select Section1 union all
                   select Section2 union all
                   select Section3
                 ) d (SectionValue)                
                 group by SectionValue
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
  = 'select category, '+@colspivot+'
      from
      (
        select 
          category,
          value
        from yourtable 
        unpivot
        (
          value
          for cols in (Section1, Section2, Section3)
        ) un
      ) x
      pivot
      (
        count(value)
        for value in ('+ @colspivot +')
      ) p'

exec sp_executesql @query 

请参阅SQL Fiddle with Demo

如果要删除未知数量的列,那么您的过程将会更复杂一些。您需要生成一个包含要取消忽略的列的字符串,您可以使用sys.columns表来获取此列表:

select @colsUnpivot 
    = stuff((select ','+quotename(C.name)
             from sys.columns as C
             where C.object_id = object_id('yourtable') and
                   C.name like 'Section%'
             for xml path('')), 1, 1, '')

然后,您需要获取新列值的列表 - 但由于这些值是动态的,我们需要生成此列表并进行一些工作。您需要将表格取消,以将值列表生成到临时表中以供使用。创建临时表以存储值:

create table #Category_Section
(
    Category varchar(50),
    SectionValue varchar(50)
);

使用您需要的数据加载临时表:

set @unpivotquery 
  = 'select 
        category,
        value
      from yourtable 
      unpivot
      (
        value for cols in ('+ @colsUnpivot +')
      ) u'

insert into #Category_Section exec(@unpivotquery);

SQL Fiddle with Demo。您将看到您的数据与上面的静态版本相同。现在,您需要使用临时表中的值创建一个字符串,该值将在最终查询中使用:

select @colsPivot 
        = STUFF((SELECT ',' + quotename(SectionValue)
                 from #Category_Section
                 group by SectionValue
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

完成所有这些后,您可以将它们组合成最终查询:

DECLARE @colsUnpivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX),
    @unpivotquery  AS NVARCHAR(MAX);

select @colsUnpivot 
    = stuff((select ','+quotename(C.name)
             from sys.columns as C
             where C.object_id = object_id('yourtable') and
                   C.name like 'Section%'
             for xml path('')), 1, 1, '');

create table #Category_Section
(
    Category varchar(50),
    SectionValue varchar(50)
);

set @unpivotquery 
  = 'select 
        category,
        value
      from yourtable 
      unpivot
      (
        value for cols in ('+ @colsUnpivot +')
      ) u';

insert into #Category_Section exec(@unpivotquery);

select @colsPivot 
        = STUFF((SELECT ',' + quotename(SectionValue)
                 from #Category_Section
                 group by SectionValue
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
  = 'select category, '+@colspivot+'
      from
      (
        select 
          category,
          value
        from yourtable 
        unpivot
        (
          value
          for cols in ('+ @colsunpivot +')
        ) un
      ) x
      pivot
      (
        count(value)
        for value in ('+ @colspivot +')
      ) p'

exec sp_executesql @query 

SQL Fiddle with Demo。所有版本都将为您提供最终结果:

|   CATEGORY | BIG | DAMAGED | GREEN | WHITE |
|------------|-----|---------|-------|-------|
| Category 1 |   2 |       0 |     2 |     2 |
| Category 2 |   1 |       1 |     0 |     1 |
| Category 3 |   1 |       1 |     0 |     1 |

如果您的值存储在单独的表中,那么您将从该表生成值列表:

DECLARE @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX);


select @colsPivot 
        = STUFF((SELECT ',' + quotename(decription)
                 from descriptions             
                 group by decription
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
  = 'select category, '+@colspivot+'
      from
      (
        select 
          category,
          value
        from yourtable 
        unpivot
        (
          value
          for cols in (Section1, Section2, Section3)
        ) un
      ) x
      pivot
      (
        count(value)
        for value in ('+ @colspivot +')
      ) p'

exec sp_executesql @query 

请参阅SQL Fiddle with Demo并仍然得到相同的结果:

|   CATEGORY | BIG | DAMAGED | GREEN | WHITE |
|------------|-----|---------|-------|-------|
| Category 1 |   2 |       0 |     2 |     2 |
| Category 2 |   1 |       1 |     0 |     1 |
| Category 3 |   1 |       1 |     0 |     1 |

答案 1 :(得分:1)

select category,
       SUM(CASE when section1='white' then 1 when section2='white' then 1 when section3='white' then 1 else 0 end) as white,
       SUM(CASE when section1='green' then 1 when section2='green' then 1 when section3='green' then 1 else 0 end) as green,
       SUM(CASE when section1='damaged' then 1 when section2='damaged' then 1 when section3='damaged' then 1 else 0 end) as damaged,
       SUM(CASE when section1='big' then 1 when section2='big' then 1 when section3='big' then 1 else 0 end) as big
from test
group by category

SQLFiddle

您可以将更多内容扩展到n个部分值,如上面的gor section1,section2,section3

所示