即使零行匹配,也让 SELECT 返回一个常量值

Nat*_*iss 18 postgresql null select

考虑这个选择语句:

SELECT *, 
       1 AS query_id 
FROM players 
WHERE username='foobar';
Run Code Online (Sandbox Code Playgroud)

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

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

顺便说一句,它是 PostgreSQL 8.4。

a_h*_*ame 24

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');
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

但我认为显式 CTE ( with...) 更具可读性(尽管在旁观者眼中总是如此)。

  • 回复:不需要“union”子句的“all”修饰符:`UNION ALL`有时比`UNION`更有效,因为您明确告诉查询规划器您希望不会有重复的行出现在`UNION`ed 查询中,或者如果有你想要它们被输出。如果没有 `ALL` 修饰符,它会假设您希望删除重复行(每个行只返回一个),就像使用 `DISTINCT` 关键字一样,并保证它可能需要额外重新扫描结果。因此,除非您特别需要输出行重复数据删除,否则将“ALL”与“UNION”一起使用。 (4认同)
  • @NatWeiss:如果你需要一个特定的订单,你**必须**提供一个`order by`。第二个“创建”一个只有一行和一列的虚拟表,并对其进行外部连接(没有任何“真实”连接条件),因此您总是至少返回那一行。在生产代码中使用 `select *` 是不好的风格。不要这样做。始终列出您需要的列。`select *` 应该*仅*用于即席查询。 (2认同)
  • @NatWeiss:您指的是“其他连接”的“替代语法”。为什么你认为 `left join` 不可读? (2认同)
  • @NatWeiss:where 子句中的隐式连接是糟糕的编码风格,应该避免。它可能会导致不需要的笛卡尔连接,而不会出现错误。并且它清楚地分离了连接和过滤这两个(关系)概念 (2认同)

Dav*_*dge 7

如果您只期望返回一行或零行,那么这也可以:

SELECT
  max(col1) col1,
  max(col2) col2, 
  1 AS query_id 
FROM
  players 
WHERE
  username='foobar';
Run Code Online (Sandbox Code Playgroud)

如果没有找到任何行,这将返回除 query_id 之外的所有值都为 null 的一行。

  • 不错的把戏。唯一的缺点是 col1 和 col2 的值可能不属于同一行,如果有多个匹配条件`username = 'foobar'` (2认同)