Can the operation shown in the following image be achieved elegantly in pure SQL and without hardcoding names into the statement?
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
答案 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
上面的演示是在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