即使零行匹配,也让SELECT返回恒定值


15

考虑以下选择语句:

SELECT *, 
       1 AS query_id 
FROM players 
WHERE username='foobar';

它返回query_id带有值的列1以及玩家的其他列。

如何将一个至少让上面的SQL回报query_id1,即使选择,反而找不到行那场比赛?

顺便说一句,它是PostgreSQL 8.4。

Answers:


22
SELECT col1, 
       col2, 
       col3, 
       1 AS query_id 
FROM players 
WHERE username='foobar'
union all 
select null,
       null,
       null,
       1
where not exists (select 1 from players where username = 'foobar');

或作为替代方案(可能会更快,因为不需要第二个子选择):

with qid (query_id) as (
   values (1)
) 
select p.*, 
       qid.query_id
from qid 
  left join players as p on (p.useranme = 'foobar');

您可以将以上内容重写为更“紧凑”的表示形式:

select p.*, 
       qid.query_id
from (values (1)) as qid (query_id)
  left join players as p on (p.useranme = 'foobar');

但我认为,显式的CTE(with...)更具可读性(尽管在情人眼中总是如此)。


1
在尝试第一个示例时,似乎不需要ALL关键字了吗?
Nathanael Weiss 2013年

2
@NatWeiss:如果需要特定的订单,必须提供order by。第二个“创建”一个只包含一行和一列的虚拟表,并对其进行外部联接(没有任何“真实”联接条件),因此您总是至少要获得那一行。select *在生产代码中使用是不好的风格。不要这样 始终列出您需要的列。select *应该 在临时查询中使用。
a_horse_with_no_name 2013年

2
@NatWeiss:您指的是“其他联接”的“替代语法”。您为什么left join不可读?
a_horse_with_no_name

2
@NatWeiss:where子句中的隐式联接是不良的编码样式,应避免使用。可能会导致不必要的笛卡尔联接,而不会给您错误。它清楚地将
联接

4
re:不需要“ union”子句的“ all”修饰符:UNION ALL有时可能比效率更高UNION,因为您明确地告诉查询计划者您希望UNIONed查询中不会出现重复的行,或者您希望它们被输出。如果没有ALL修饰符,则假定您要删除重复的行(仅返回每行中的一条),与DISTINCT关键字非常相似,并确保可能需要对额外的结果进行重新扫描。因此ALLUNION除非您特别需要输出行重复数据删除,否则请与with一起使用。
David Spillett

7

如果您只希望返回一或零行,那么这也将起作用:

SELECT
  max(col1) col1,
  max(col2) col2, 
  1 AS query_id 
FROM
  players 
WHERE
  username='foobar';

如果未找到行,则将返回一行,所有值均为null,但query_id除外。


2
好招 唯一的缺点是,如果有多个匹配条件,则col1和col2的值可能不属于同一行username = 'foobar'
a_horse_with_no_name 2013年

1
可以以这种方式使用coalesce()吗?
Nathanael Weiss 2013年

1
合并不会生成表中没有投影的行。
David Aldridge

1
@a_horse_with_no_name是的,尽管表名和列名表明该谓词在该表的候选键上,所以将投影零行或一行。
David Aldridge

3

在这里晚了一些时间,但是这里有一个有效的语法(至少在9.2中,没有尝试过较早的版本)。

SELECT (COALESCE(a.*,b.*::players)).*
FROM ( SELECT col1,  col2,  col3, 1 AS query_id 
       FROM players WHERE username='foobar' ) a
RIGHT JOIN (select null col1, null col2, null col3, 1 col4) b
ON a.query_id = b.col4;

如果“ a”的全部内容为空,将仅返回“空白”行。

请享用。/位头


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.