SQL查询动态列的方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL查询动态列的方法相关的知识,希望对你有一定的参考价值。

有一个表,列名为包括name varchar(10),month_t varchar(7),D1 int,D2 int,D3 int,D4 int,D5 int,D6 int,D7 int ......D31 int 共33列。
现在给一个变量@day int,
当这个变量为1时,执行查询 select * from 表 where D1=1
当这个变量为2时,执行查询 select * from 表 where D2=2
当这个变量为3时,执行查询 select * from 表 where D3=3
......
当这个变量为31时,执行查询 select * from 表 where D31=31

请问这个存储过程怎么写??
fangliang911的猜想完全正确。

参考技术A 首先,这张表本身就很不合理,如果不是硬要用这种结构,推荐您使用一列存储数据,我有点明白您可能是要最后通过select生成一张月报表,不知我的猜想对吗?(好像还是没搞清楚)

就事论事吧
create proc xxx @day int
as
if @day=1 select * from 表 where D1=@day
if @day=2 select * from 表 where D2=@day
if @day=3 select * from 表 where D3=@day
if @day=4 select * from 表 where D4=@day
后面就不写了,共要31行,恐怕也是最好想出的办法了
参考技术B 你这貌似 只能一个一个判断:
create procedure pro_info
@day int
as
if @day=1
begin
select * from 表 where d1=1
end
if.....
参考技术C Oracle 数据库:

create or replace PROCEDURE XXX(
day INTEGER;
)IS
v_sql VARCHAR;
BEGIN
v_sql := 'select * from 表 where D'||to_char(day)||'='||to_char(day);
Execute immediate v_sql;
END;
参考技术D create proc xxx @day int
as
declare @D int
set @D=@D+@day
select * from 表 where @D=@day
第5个回答  2008-07-03 晕,有这样建库的?

SQL Server 2005 中带有动态列的交叉表查询

【中文标题】SQL Server 2005 中带有动态列的交叉表查询【英文标题】:Crosstab Query with Dynamic Columns in SQL Server 2005 up 【发布时间】:2012-09-07 12:33:23 【问题描述】:

我在 SQL Server 中遇到交叉表查询问题。

假设我有如下数据:

| ScoreID | StudentID |      Name |    Sex | SubjectName | Score |
------------------------------------------------------------------
|       1 |         1 | Student A |   Male |           C |   100 |
|       2 |         1 | Student A |   Male |         C++ |    40 |
|       3 |         1 | Student A |   Male |     English |    60 |
|       4 |         1 | Student A |   Male |    Database |    15 |
|       5 |         1 | Student A |   Male |        Math |    50 |
|       6 |         2 | Student B |   Male |           C |    77 |
|       7 |         2 | Student B |   Male |         C++ |    12 |
|       8 |         2 | Student B |   Male |     English |    56 |
|       9 |         2 | Student B |   Male |    Database |    34 |
|      10 |         2 | Student B |   Male |        Math |    76 |
|      11 |         3 | Student C | Female |           C |    24 |
|      12 |         3 | Student C | Female |         C++ |    10 |
|      13 |         3 | Student C | Female |     English |    15 |
|      14 |         3 | Student C | Female |    Database |    40 |
|      15 |         3 | Student C | Female |        Math |    21 |
|      16 |         4 | Student D | Female |           C |    17 |
|      17 |         4 | Student D | Female |         C++ |    34 |
|      18 |         4 | Student D | Female |     English |    24 |
|      19 |         4 | Student D | Female |    Database |    56 |
|      20 |         4 | Student D | Female |        Math |    43 |

我想查询结果如下:

| StuID| Name      | Sex    | C  | C++ | Eng | DB | Math | Total | Average |
|  1   | Student A | Male   | 100|  40 | 60  | 15 |  50  |  265  |   54    |
|  2   | Student B | Male   | 77 |  12 | 56  | 34 |  76  |  255  |   51    |
|  3   | Student C | Female | 24 |  10 | 15  | 40 |  21  |  110  |   22    |
|  4   | Student D | Female | 17 |  34 | 24  | 56 |  43  |  174  |   34.8  |

如何查询以显示这样的输出? 注意:

主题名称:

C C++ 英语 数据库

数学

将根据学生所学的科目而改变。

请到http://sqlfiddle.com/#!6/2ba07/1测试这个查询。

【问题讨论】:

【参考方案1】:

有两种方法可以执行PIVOT 静态方法,其中您对值进行硬编码,而动态方法则在您执行时确定列。

即使您需要动态版本,有时从静态的PIVOT 开始,然后转向动态版​​本会更容易。

静态版本:

SELECT studentid, name, sex,[C], [C++], [English], [Database], [Math], total, average
from 
(
  select s1.studentid, name, sex, subjectname, score, total, average
  from Score s1
  inner join
  (
    select studentid, sum(score) total, avg(score) average
    from score
    group by studentid
  ) s2
    on s1.studentid = s2.studentid
) x
pivot 
(
   min(score)
   for subjectname in ([C], [C++], [English], [Database], [Math])
) p

见SQL Fiddle with demo

现在,如果您不知道要转换的值,那么您可以使用动态 SQL:

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

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



set @query = 'SELECT studentid, name, sex,' + @cols + ', total, average
              from 
             (
                select s1.studentid, name, sex, subjectname, score, total, average
                from Score s1
                inner join
                (
                  select studentid, sum(score) total, avg(score) average
                  from score
                  group by studentid
                ) s2
                  on s1.studentid = s2.studentid
            ) x
            pivot 
            (
                min(score)
                for subjectname in (' + @cols + ')
            ) p '

execute(@query)

见SQL Fiddle with Demo

两个版本将产生相同的结果。

只是为了完善答案,如果您没有 PIVOT 函数,那么您可以使用 CASE 和聚合函数获得此结果:

select s1.studentid, name, sex, 
  min(case when subjectname = 'C' then score end) C,
  min(case when subjectname = 'C++' then score end) [C++],
  min(case when subjectname = 'English' then score end) English,
  min(case when subjectname = 'Database' then score end) [Database],
  min(case when subjectname = 'Math' then score end) Math,
  total, average
from Score s1
inner join
(
  select studentid, sum(score) total, avg(score) average
  from score
  group by studentid
) s2
  on s1.studentid = s2.studentid
group by s1.studentid, name, sex, total, average

见SQL Fiddle with Demo

【讨论】:

非常感谢您的解决方案。这就是我需要的。再次感谢。 在分配@cols 时,有什么办法可以按字母顺序对subjectName 进行排序? @Sunny 是的,您可以同时使用GROUP BYORDER BY,而不是使用DISTINCT,以按任意顺序对名称进行排序——参见降序演示——sqlfiddle.com/#!6/2ba07/176 【参考方案2】:

在这种情况下,您需要使用 SQL PIVOT。请参考以下链接:

Pivot on Unknown Number of Columns

Pivot two or more columns in SQL Server

Pivots with Dynamic Columns in SQL Server

【讨论】:

【参考方案3】:

这需要在运行时构建 SQL 查询字符串。 SQL Server 中的列名、计数和数据类型始终是静态的(最重要的原因是优化器在优化时必须知道查询数据流)。

所以我建议你在运行时构建一个PIVOT-query 并通过sp_executesql 运行它。请注意,您必须对枢轴列值进行硬编码。小心正确地逃脱它们。您不能为它们使用参数。

或者,您可以为每个列数构建一个这样的查询,并将参数仅用于枢轴值。您必须分配一些虚拟列名称,例如Pivot0, Pivot1, ...。对于每列计数,您仍然需要一个查询模板。除非您愿意将最大数量的枢轴列硬编码到查询中(比如 20)。在这种情况下,您实际上可以使用静态 SQL。

【讨论】:

投反对票的人是否愿意发表评论?这个答案是正确的,AFAIK。

以上是关于SQL查询动态列的方法的主要内容,如果未能解决你的问题,请参考以下文章

如何动态准备 SQL 查询(也包括列名)避免 SQL 注入

SQL mybatis动态查询小结

动态 SQL 透视查询中的分组和聚合函数

在SQL查询结果中添加自增列的两种方法

T-SQL动态查询——动态SQL

使用dapper时动态拼接查询sql有啥好的方法吗