在PostgreSQL中,我想根据某些条件选择一行,但如果没有符合条件的行,我想返回第一行。该表实际上包含一个序数列,因此任务应该更容易(第一行是序号为0的那一行)。例如:
SELECT street, zip, city
FROM address
WHERE street LIKE 'Test%' OR ord = 0
LIMIT 1;
但是在这种情况下,没有办法保证匹配的记录的顺序,我没有任何东西可以按顺序排序。使用单个SELECT
语句执行此操作的方法是什么?
答案 0 :(得分:5)
你走在正确的轨道上。只需添加order by
:
SELECT street, zip, city
FROM address
WHERE street LIKE 'Test%' OR ord = 0
ORDER BY (CASE WHEN street LIKE 'Test%' THEN 1 ELSE 0 END) DESC
LIMIT 1;
或者,或者:
ORDER BY ord DESC
其中任何一个都会将ord = 0
行放在最后。
编辑:
Erwin提出了一个很好的观点,即从索引使用的角度来看,OR
子句中的WHERE
不是最好的方法。我会修改我的答案:
SELECT *
FROM ((SELECT street, zip, city
FROM address
WHERE street LIKE 'Test%'
LIMIT 1
)
UNION ALL
(SELECT street, zip, city
FROM address
WHERE ord = 0
LIMIT 1
)
) t
ORDER BY (CASE WHEN street LIKE 'Test%' THEN 1 ELSE 0 END) DESC
LIMIT 1;
这允许查询使用两个索引(street
和ord
)。请注意,这只是因为LIKE
模式不以通配符开头。如果LIKE
模式以通配符开头,则此查询形式仍将执行全表扫描。
答案 1 :(得分:3)
我想根据某些条件选择一行,但如果没有行 符合标准,我想返回第一行
您根本不需要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
BTW,这是一个单语句。
这更详细,但允许更简单的查询计划。如果第一个SELECT
产生足够的行(在我们的例子中:1),则永远不会执行UNION ALL
的第二个SELECT
。如果您使用EXPLAIN ANALYZE
进行测试,则会在查询计划中看到(never executed)
。
详细说明:
UNION ALL
回复戈登的评论。 Per documentation:
评估同一
UNION
语句中的多个SELECT
运算符 从左到右,除非括号另有说明。
大胆强调我的
并LIMIT
使Postgres在找到足够的行后立即停止评估。这就是为什么您在(never executed)
的输出中看到EXPLAIN ANALYZE
的原因。
如果您在最终ORDER BY
之前添加外部LIMIT
,则无法进行此优化。然后必须收集所有行,以查看哪些行可能先排序。
答案 2 :(得分:2)
这样的事情怎么样......(我对PostgreSQL并不熟悉,所以语法可能略有偏差)
SELECT street, zip, city, 1 as SortOrder
FROM address
WHERE street LIKE 'Test%'
--
union all
--
SELECT street, zip, city, 2 as SortOrder
FROM address
WHERE ord = 0
ORDER BY SortOrder
LIMIT 1;
答案 3 :(得分:0)
您可以执行以下操作:
SELECT street, zip, city
FROM address
WHERE (EXISTS(SELECT * FROM address WHERE street LIKE 'Test%') AND street LIKE 'Test%') OR
(NOT EXISTS(SELECT * FROM address WHERE street LIKE 'Test%') AND ord = 0)