3 postgresql rows sql-order-by selection window-functions
我有一张这样的表:
a | user_id
----------+-------------
0.1133 | 2312882332
4.3293 | 7876123213
3.1133 | 2312332332
1.3293 | 7876543213
0.0033 | 2312222332
5.3293 | 5344343213
3.2133 | 4122331112
2.3293 | 9999942333
Run Code Online (Sandbox Code Playgroud)
我想找到一个特定的行 -1.3293 | 7876543213例如 - 并选择最近的 4 行。上面 2 个,如果可能的话,下面 2 个。
排序顺序是 ORDER BY a ASC。
在这种情况下,我将得到:
0.0033 | 2312222332
0.1133 | 2312882332
2.3293 | 9999942333
3.1133 | 2312332332
Run Code Online (Sandbox Code Playgroud)
如何使用 PostgreSQL 实现这一目标?(顺便说一句,我正在使用 PHP。)
PS:对于最后一行或第一行,最近的行将是上面 4 行或下面 4 行。
CREATE TEMP TABLE tbl(a float, user_id bigint);
INSERT INTO tbl VALUES
(0.1133, 2312882332)
,(4.3293, 7876123213)
,(3.1133, 2312332332)
,(1.3293, 7876543213)
,(0.0033, 2312222332)
,(5.3293, 5344343213)
,(3.2133, 4122331112)
,(2.3293, 9999942333);
Run Code Online (Sandbox Code Playgroud)
WITH x AS (
SELECT a
,user_id
,row_number() OVER (ORDER BY a, user_id) AS rn
FROM tbl
), y AS (
SELECT rn, LEAST(rn - 3, (SELECT max(rn) - 5 FROM x)) AS min_rn
FROM x
WHERE (a, user_id) = (1.3293, 7876543213)
)
SELECT *
FROM x, y
WHERE x.rn > y.min_rn
AND x.rn <> y.rn
ORDER BY x.a, x.user_id
LIMIT 4;
Run Code Online (Sandbox Code Playgroud)
返回问题中描述的结果。假设这(a, user_id)是唯一的。
目前尚不清楚是否a应该是唯一的。这就是为什么我user_id另外排序以打破联系。这也是我使用window function 的原因row_number(),而不是 rank()为此。row_number()在任何情况下都是正确的工具。我们想要 4 行。rank()如果排序顺序中有对等点,则会给出未定义的行数。
只要表中至少有 5 行,这总是返回 4 行。接近第一行/最后一行,返回第一行/最后4行。在所有其他情况下之前/之后的两行。条件行本身被排除在外。
这是@Tim Landscheidt 发布的内容的改进版本。如果您喜欢索引的想法,请投票支持他的答案。不要打扰小桌子。但是会提高大表的性能- 前提是您有合适的索引。最好的选择将是一个多列索引上(a, user_id)。
WITH params(_a, _user_id) AS (SELECT 5.3293, 5344343213) -- enter params once
,x AS (
(
SELECT a
,user_id
,row_number() OVER (ORDER BY a DESC, user_id DESC) AS rn
FROM tbl, params p
WHERE a < p._a
OR a = p._a AND user_id < p._user_id -- a is not defined unique
ORDER BY a DESC, user_id DESC
LIMIT 5 -- 4 + 1: including central row
)
UNION ALL -- UNION right away, trim one query level
(
SELECT a
,user_id
,row_number() OVER (ORDER BY a ASC, user_id ASC) AS rn
FROM tbl, params p
WHERE a > p._a
OR a = p._a AND user_id > p._user_id
ORDER BY a ASC, user_id ASC
LIMIT 5
)
)
, y AS (
SELECT a, user_id
FROM x, params p
WHERE (a, user_id) <> (p._a, p._user_id) -- exclude central row
ORDER BY rn -- no need to ORDER BY a
LIMIT 4
)
SELECT *
FROM y
ORDER BY a, user_id -- ORDER result as requested
Run Code Online (Sandbox Code Playgroud)
根据问题(a, user_id)形式的搜索条件,而不仅仅是a. 这以微妙的不同方式改变了窗口框架ORDER BY和WHERE子句。
UNION马上,不需要额外的查询级别。您需要在两个 UNION 查询周围加上括号以允许单独的ORDER BY.
按要求对结果进行排序。需要另一个查询级别(几乎没有任何成本)。
由于参数在多个地方使用,我将输入集中在领先的 CTE 中。
为了重复使用,您可以几乎“按原样”将此查询包装到 SQL 或 plpgsql 函数中。
| 归档时间: |
|
| 查看次数: |
1666 次 |
| 最近记录: |