我想根据某些条件选择一行,但如果没有行
符合条件,我想返回第一行
更短(且正确)
您实际上根本不需要WHERE 子句:
SELECT street, zip, city
FROM address
ORDER BY street !~~ 'Test%', ord
LIMIT 1;
!~~ 只是 NOT LIKE 的 Postgres 运算符。你可以使用任何一个。请注意,通过反转逻辑(NOT LIKE 而不是LIKE),我们现在可以使用默认的ASC排序顺序和NULL 排序最后,这可能很重要。继续阅读。
这更短(但不一定更快)。它也与currently accepted answer by @Gordon 略有不同(更可靠)。
当按boolean 表达式排序时,您必须了解它的工作原理:
当前接受的答案使用ORDER BY <boolean expression> DESC,它将首先对 NULL 进行排序。在这种情况下,您通常应该添加NULLS LAST:
如果street 定义为NOT NULL,这显然是无关紧要的,但问题中没有定义。 (始终提供表定义。)当前接受的答案通过在 WHERE 子句中排除 NULL 值来避免问题。
其他一些 RDBMS(MySQL、Oracle 等)没有像 Postgres 这样正确的 boolean 类型,因此我们经常看到来自这些产品的人的错误建议。
您当前的查询(以及当前接受的答案)需要 WHERE 子句 - 或至少 NULLS LAST。 ORDER BY 中的不同表达方式都没有必要。
更重要的是,然而,如果多行有一个匹配的street(这是意料之中的),则返回的行将是任意的,并且可能会在调用之间发生变化 - 通常是一种不良影响。此查询选择ord 最小的行来打破平局并产生稳定的结果。
这种形式也更加灵活,因为它不依赖于ord = 0 的行的存在。取而代之的是,以任何一种方式选择具有最小 ord 的行。
索引更快
(并且仍然正确。)
对于大表,以下索引将从根本上提高此查询的性能:
CREATE INDEX address_street_pattern_ops_idx ON address(street text_pattern_ops);
详细解释:
根据未定义的详细信息,可能需要向索引添加更多列。
使用此索引的最快查询:
(
SELECT street, zip, city
FROM address
WHERE street LIKE 'Test%'
ORDER BY ord -- or something else?
-- LIMIT 1 -- you *could* add LIMIT 1 in each leg
)
UNION ALL
(
SELECT street, zip, city
FROM address
ORDER BY ord
-- LIMIT 1 -- .. but that's not improving anything in *this* case
)
LIMIT 1
顺便说一句,这是一个单个语句。
这更详细,但允许更简单的查询计划。如果第一个 SELECT 产生足够的行(在我们的例子中:1),则永远不会执行 UNION ALL 的第二个 SELECT。如果您使用EXPLAIN ANALYZE 进行测试,您将在查询计划中看到(never executed)。
详情:
评估UNION ALL
回复戈登的评论。 Per documentation:
对同一 SELECT 语句中的多个 UNION 运算符求值
从左到右,除非括号中另有说明。
我的大胆强调。
并且LIMIT 使 Postgres 在找到足够的行后立即停止评估。这就是为什么您会在EXPLAIN ANALYZE 的输出中看到(never executed)。
如果您在最终的LIMIT 之前添加外部ORDER BY,则无法进行此优化。然后必须收集所有行以查看哪些可能首先排序。