content of a table as columns in the result

时间:2015-05-04 19:47:14

标签: sql

Can the operation shown in the following image be achieved elegantly in pure SQL and without hardcoding names into the statement?

content of a table as columns in the result

Tables A, B and C are given. The one on the right is the result I would like to get.

The following statement

SELECT name, val_name, val
FROM A, B, C
WHERE a_id = A.id
  and b_id = B.id

returns the correct information but not in the form I would like:

foo|goos|6
foo|blobs|7
bar|goos|8
baz|blobs|9

4 个答案:

答案 0 :(得分:3)

您可以使用条件聚合

来实现
SELECT A.NAME, 
       SUM(CASE WHEN B.VAL_NAME = 'GOOS' THEN C.VAL END) AS GOOS,
       SUM(CASE WHEN B.VAL_NAME = 'BLOBS' THEN C.VAL END) AS BLOBS
FROM A 
LEFT JOIN C ON A.ID = C.A_ID
LEFT JOIN B ON C.B_ID = B.ID
GROUP BY A.NAME

SQL Fiddle Demo

上面的演示是在MySQL中。它也适用于大多数RDBMS,因为这是ANSI标准SQL。

解决方案明确假设您在编写查询时已知VAL_NAME个值。

答案 1 :(得分:1)

准备样本数据:

  create table a (
     id          number,
     name        varchar2(10)
     )
  /

  create table b (
     id          number,
     val_name    varchar2(10)
     )
  /

  create table c (
     id          number,
     a_id        number,
     b_id        number,
     val         number
     )
  /

  insert into a values ( 1, 'FOO' );
  insert into a values ( 2, 'BAR' );
  insert into a values ( 3, 'BAZ' );
  insert into a values ( 4, 'QUX' );

  insert into b values ( 1, 'GOOS' );
  insert into b values ( 2, 'BLOBS' );

  insert into c values ( 1, 1, 1, 6 );
  insert into c values ( 2, 1, 2, 7 );
  insert into c values ( 3, 2, 1, 8 );
  insert into c values ( 4, 3, 2, 9 );

  commit;

尝试此查询:

select distinct a.name, 
      first_value(case when b_goo.val_name is not null then c.val else null end) over (partition by a.name order by b_goo.val_name nulls last ) goos,
      first_value(case when b_blob.val_name is not null then c.val else null end) over (partition by a.name order by b_blob.val_name nulls last ) blobs
   from a
      left outer join c
      on c.a_id = a.id
      left outer join b  b_goo
      on b_goo.id = c.b_id
         and b_goo.val_name = 'GOOS'
      left outer join b  b_blob
      on b_blob.id = c.b_id
         and b_blob.val_name = 'BLOBS'
/

结果:

  NAME             GOOS      BLOBS
  ---------- ---------- ----------
  QUX
  BAR                 8
  FOO                 6          7
  BAZ                            9

[编辑]抱歉,你想显示ids,而不是名字,修复了这里[/ edit]

所以我先做一些外连接,使用2"副本"你的桌子B ..所以我可以在1列中有一个GOO而在另一列中有BLOB。 然后我使用FIRST_VALUE分析函数来挑出第一个非空值,这有助于折叠重复行。 DISTINCT也有帮助。

我在Oracle中这样做了,但它应该像在其他RDBMS中那样工作。

答案 2 :(得分:1)

单独谈到MS SQL Server,答案(问题是"可以优雅地")是否。

加入这三个表后,您必须透过B.VAL_NAME的内容。如果您知道值的名称是"转出"那么这只能在T-SQL中完成(在T-SQL中)。事先(我假设你没有 - 否则,你不会将它们作为显式值存储在表B中)。由于您在运行时之前不知道列的名称,因此必须使用动态SQL在运行时构建查询。它起作用了,但是所有人都告诉我这种解决方案 - 我写的太多实例 - 除了"优雅"。

如果事先知道得到的列名,@ Dittos'解决方案将起作用,你也不必混淆不合适的数据透视表。

答案 3 :(得分:1)

尝试

select 
    a.name,
    sum(case when d.valname = 'goods' then d.val 
             else 0 end) as goods,
    sum(case when d.valname = 'blobs' then d.val 
             else 0 end) as blobs
from a
left join (
    select c.aid, c.val, b.valname
    from c
    join b
    on c.bid = b.id
) d
on d.aid = a.id
group by a.name