我有一个包含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上
由于
答案 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
如果要删除未知数量的列,那么您的过程将会更复杂一些。您需要生成一个包含要取消忽略的列的字符串,您可以使用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
您可以将更多内容扩展到n个部分值,如上面的gor section1,section2,section3
所示