使用sql选择的值作为另一个select的行名

时间:2011-01-24 17:57:21

标签: sql sql-server sql-server-2008

在MSSQL服务器上,给出表:

TABLE values {
    int id;
    timestamp date;
    int value;
}

TABLE value_type {
    int value_id; // foreign key on values.id
    text name;
}

我可以发出如下查询:

SELECT date, name, value
FROM values
LEFT JOIN value_type ON value_id = id;

得到类似的东西:

+----------+------+-------+
| date     | name | value |
+----------+------+-------+
| 01.01.10 | foo  |  1.0  |
+----------+------+-------+
| 01.01.10 | bar  |  2.0  |
+----------+------+-------+
| 02.01.10 | bar  |  4.0  |
+----------+------+-------+
| 03.01.10 | foo  |  5.0  |
+----------+------+-------+

如果我想获取这样的数据,查询数据库的最有效方法是什么?

+----------+------+-------+
| date     | foo  | bar   |
+----------+------+-------+
| 01.01.10 | 1.0  |  2.0  |
+----------+------+-------+
| 02.01.10 | NULL |  4.0  |
+----------+------+-------+
| 03.01.10 | 5.0  |  NULL |
+----------+------+-------+

数据库结构要复杂得多,不幸的是我无法改变它。

编辑:解决方案不应将foobar用作查询中的硬编码值。

4 个答案:

答案 0 :(得分:2)

您可以在日期group by

SELECT  Date
,       max(case when vt.name = 'foo' then v.value end) as Foo
,       max(case when vt.name = 'bar' then v.value end) as Bar
FROM Values v
LEFT JOIN 
        value_type vt
ON      vt.value_id = v.id
group by
        Date

顺便说一句,外键似乎不寻常。我希望value_type表中的values列,而不是value_id表中的values_type列!有比值类型更多的值,对吗?

编辑:SQL无法生成可变数量的列,因此您将不得不求助于meta-SQL或动态SQL。这是一个例子:

create table [values] (id int, date datetime, value float)
insert [values] values (1, '2010-01-01', 1.0)
insert [values] values (2, '2010-01-01', 2.0)
insert [values] values (3, '2010-01-02', 4.0)
insert [values] values (4, '2010-01-03', 5.0)
create table value_types (value_id int, name varchar(max))
insert value_types values (1,'foo'), (2,'bar'), (3,'bar'), (4,'foo')

declare @sql varchar(max)
set @sql = ''
select  @sql = @sql + ',       max(case when vt.name = ''' + 
                   name + ''' then v.value end) as ' + name + CHAR(13) + CHAR(10)
from    value_types
group by
        name

set @sql = 'SELECT  Date
' + @sql + 
'FROM   [values] v
LEFT JOIN 
        value_types vt
ON      vt.value_id = v.id
group by
        Date'

exec (@sql)

打印:

Date        bar     foo
----------- ------- -------
2010-01-01  2       1
2010-01-02  4       NULL
2010-01-03  NULL    5

答案 1 :(得分:2)

对于SQL Server 2005+,您可以使用动态SQL和PIVOT

DECLARE @ValuesNames NVARCHAR(4000), @Query NVARCHAR(MAX)
SET @ValuesNames = ''

SELECT @ValuesNames = @ValuesNames + QUOTENAME([name]) + ','
FROM [values] A
INNER JOIN value_type B
ON B.value_id = A.id
GROUP BY [name]
ORDER BY [name]

SET @ValuesNames = LEFT(@ValuesNames,LEN(@ValuesNames)-1)

SET @Query = '
SELECT [date], '+@ValuesNames+'
FROM (  SELECT [date], [name], value
        FROM [values]
        LEFT JOIN value_type ON value_id = id) A
PIVOT(SUM(value) FOR [name] IN ('+@ValuesNames+')) AS PT
'
EXEC sp_executesql @Query

答案 2 :(得分:0)

根据新要求更新: 您需要为查询动态生成SQL语句,或者如果要将其加载到应用程序中,则可以转换原始查询或类似查询中的数据。

答案 3 :(得分:0)

未经测试:你可以定义一个视图来连接pk-fk对上的表,然后尝试这样的事情:

Select date, 

case name when "foo" then value else null end as foo, 

case name when "base" then value else null end as bar 

from mytable  

order by date

您需要提前知道可能值的范围。

为了使其更灵活,您可以从存储过程中“name”的不同值构建动态case语句,并在此字符串上调用exec()。