我想根据特定条件选择一些行,然后从该集合中取一个条目,在它之前和之后取5行。
现在,如果表上有主键,我可以用数字方式执行此操作(例如,主键在数字上比目标行的键小5,比目标行的键多5个。)
因此,选择主键为7的行和附近的行:
select primary_key from table where primary_key > (7-5) order by primary_key limit 11;
2
3
4
5
6
-=7=-
8
9
10
11
12
但是如果我只选择某些行开头,我会失去使用主键的数字方法(假设键无论如何都没有任何间隙),并且需要另一种方法来获得最接近的行某个目标行之前和之后的行。
这样一个选择的主键输出看起来可能更随机,因此数学定位的成功率较低(因为某些结果会被过滤掉,例如使用where active=1
):
select primary_key from table where primary_key > (34-5)
order by primary_key where active=1 limit 11;
30
-=34=-
80
83
100
113
125
126
127
128
129
请注意,由于条件(例如因为有许多非活动项)的示例导致主键中的间隙,我不再得到最接近的5以上和5以下相反,我得到最接近的1和最接近的9,而不是。
答案 0 :(得分:22)
如果使用编程语言运行两个查询,有很多方法可以做到这一点,但这是在一个SQL查询中执行此操作的一种方法:
(SELECT * FROM table WHERE id >= 34 AND active = 1 ORDER BY id ASC LIMIT 6)
UNION
(SELECT * FROM table WHERE id < 34 AND active = 1 ORDER BY id DESC LIMIT 5)
ORDER BY id ASC
这将返回上面的5行,目标行和下面的5行。
答案 1 :(得分:7)
这是另一种使用分析函数超前和滞后的方法。如果我们可以在WHERE子句中使用分析函数,那将是很好的。所以你需要使用子查询或CTE。这是一个适用于pagila示例数据库的示例。
WITH base AS (
SELECT lag(customer_id, 5) OVER (ORDER BY customer_id) lag,
lead(customer_id, 5) OVER (ORDER BY customer_id) lead,
c.*
FROM customer c
WHERE c.active = 1
AND c.last_name LIKE 'B%'
)
SELECT base.* FROM base
JOIN (
-- Select the center row, coalesce so it still works if there aren't
-- 5 rows in front or behind
SELECT COALESCE(lag, 0) AS lag, COALESCE(lead, 99999) AS lead
FROM base WHERE customer_id = 280
) sub ON base.customer_id BETWEEN sub.lag AND sub.lead
sgriffinusa的解决方案的问题是你不知道你的中心行最终会是哪个row_number。他认为这将是第30行。
答案 2 :(得分:1)
对于类似的查询,我使用没有CTE的分析函数。类似的东西:
select ...,
LEAD(gm.id) OVER (ORDER BY Cit DESC) as leadId,
LEAD(gm.id, 2) OVER (ORDER BY Cit DESC) as leadId2,
LAG(gm.id) OVER (ORDER BY Cit DESC) as lagId,
LAG(gm.id, 2) OVER (ORDER BY Cit DESC) as lagId2
...
where id = 25912
or leadId = 25912 or leadId2 = 25912
or lagId = 25912 or lagId2 = 25912
这样的查询对我来说比加入CTE更快(来自Scott Bailey的回答)。但当然不那么优雅
答案 3 :(得分:0)
你可以利用row_number()(自8.4起)。这可能不是正确的语法(不熟悉postgresql),但希望这个想法能够得到说明:
SELECT *
FROM (SELECT ROW_NUMBER() OVER (ORDER BY primary_key) AS r, *
FROM table
WHERE active=1) t
WHERE 25 < r and r < 35
这将生成具有连续数字的第一列。您可以使用它来识别单行及其上方和下方的行。
答案 4 :(得分:0)
如果您想以“关系纯粹”的方式进行,您可以编写一个对行进行排序和编号的查询。像:
select (
select count(*) from employees b
where b.name < a.name
) as idx, name
from employees a
order by name
然后将其用作公用表表达式。编写一个选择,将其过滤到您感兴趣的行,然后使用一个标准将其连接回自身,该标准是表的右侧副本的索引不大于或大于索引的索引。在左边排。仅在右侧的行上进行投影。像:
with numbered_emps as (
select (
select count(*)
from employees b
where b.name < a.name
) as idx, name
from employees a
order by name
)
select b.*
from numbered_emps a, numbered_emps b
where a.name like '% Smith' -- this is your main selection criterion
and ((b.idx - a.idx) between -5 and 5) -- this is your adjacency fuzzy-join criterion
什么可以更简单!
我认为基于行数的解决方案会更快。