表中“下一个”和“上一个”的 SQL 是啥?

Posted

技术标签:

【中文标题】表中“下一个”和“上一个”的 SQL 是啥?【英文标题】:What is the SQL for 'next' and 'previous' in a table?表中“下一个”和“上一个”的 SQL 是什么? 【发布时间】:2008-10-15 00:13:06 【问题描述】:

我有一个项目表,每个项目都有一个与之关联的日期。如果我有与一个项目关联的日期,如何使用 SQL 查询数据库以获取表中的“上一个”和“后续”项目?

不可能简单地添加(或减去)一个值,因为日期之间没有常规间隔。

一种可能的应用是相册或博客 Web 应用中的“上一个/下一个”链接,其中基础数据位于 SQL 表中。

我认为有两种可能的情况:

首先每个日期都是唯一的:

样本数据:

1,3,8,19,67,45

当提供 8 作为参数时,哪个查询(或多个查询)会给出 3 和 19? (或第 3、8、19 行)。请注意,并不总是要返回三行 - 在序列的末尾会丢失一。

其次,如果有一个单独的唯一键来对元素进行排序,那么返回“围绕”日期的集合的查询是什么?预期的顺序是按日期然后键。

样本数据:

(key:date) 1:1,2:3,3:8,4:8,5:19,10:19,11:67,15:45,16:8

“8”的查询返回集合:

2:3,3:8,4:8,16:8,5:19

或者什么查询生成表:

key date prev-key next-key
1   1    null     2
2   3    1        3
3   8    2        4
4   8    3        16
5   19   16       10
10  19   5        11
11  67   10       15
15  45   11       null
16  8    4        5

表格顺序并不重要 - 只是 next-key 和 prev-key 字段。


TheSoftwareJedi 和 Cade Roux 都有适用于我昨晚发布的数据集的解决方案。对于第二个问题,这两个数据集似乎都失败了:

(key:date) 1:1,2:3,3:8,4:8,5:19,10:19,11:67,15:45,16:8

预期的顺序是按日期然后键,所以一个预期的结果可能是:

2:3,3:8,4:8,16:8,5:19

还有一个:

key date prev-key next-key
1   1    null     2
2   3    1        3
3   8    2        4
4   8    3        16
5   19   16       10
10  19   5        11
11  67   10       15
15  45   11       null
16  8    4        5

表格顺序并不重要 - 只是 next-key 和 prev-key 字段。

【问题讨论】:

澄清一下,您只希望返回 3 行(在您的示例中)?一个上,一个下,一个提供? 当用于具有许多列的表时,这会变得更加复杂,并且您想选择 *.如果您“分页”所在的列中可能存在重复值,则复杂性会再次增加。 嗯嗯,就像我说的,你必须让它变得更复杂,不是吗? :) 我正在尝试same thing via Entity Framework。 【参考方案1】:

Select max(element) From Data Where Element

联合

Select min(element) From Data Where Element > 8

但通常将 sql 用于面向集合的操作而不是迭代操作更有用。

【讨论】:

【参考方案2】:

自加入。

对于表:

/*
CREATE TABLE [dbo].[***_203302](
    [val] [int] NOT NULL
) ON [PRIMARY]
*/

带参数@val

SELECT cur.val, MAX(prv.val) AS prv_val, MIN(nxt.val) AS nxt_val
FROM ***_203302 AS cur
LEFT JOIN ***_203302 AS prv
    ON cur.val > prv.val
LEFT JOIN ***_203302 AS nxt
    ON cur.val < nxt.val
WHERE cur.val = @val
GROUP BY cur.val

您可以将其设为带有输出参数的存储过程,或者将其作为相关子查询加入到您要提取的数据中。

如果没有参数,对于您的数据,结果将是:

val         prv_val     nxt_val
----------- ----------- -----------
1           NULL        3
3           1           8
8           3           19
19          8           45
45          19          67
67          45          NULL

对于修改后的示例,您将其用作相关子查询:

/*
CREATE TABLE [dbo].[***_203302](
    [ky] [int] NOT NULL,
    [val] [int] NOT NULL,
    CONSTRAINT [PK_***_203302] PRIMARY KEY CLUSTERED (
        [ky] ASC
    )
)
*/

SELECT cur.ky AS cur_ky
        ,cur.val AS cur_val
        ,prv.ky AS prv_ky
        ,prv.val AS prv_val
        ,nxt.ky AS nxt_ky
        ,nxt.val as nxt_val
FROM (
    SELECT cur.ky, MAX(prv.ky) AS prv_ky, MIN(nxt.ky) AS nxt_ky
    FROM ***_203302 AS cur
    LEFT JOIN ***_203302 AS prv
        ON cur.ky > prv.ky
    LEFT JOIN ***_203302 AS nxt
        ON cur.ky < nxt.ky
    GROUP BY cur.ky
) AS ordering
INNER JOIN ***_203302 as cur
    ON cur.ky = ordering.ky
LEFT JOIN ***_203302 as prv
    ON prv.ky = ordering.prv_ky
LEFT JOIN ***_203302 as nxt
    ON nxt.ky = ordering.nxt_ky

按预期输出:

cur_ky      cur_val     prv_ky      prv_val     nxt_ky      nxt_val
----------- ----------- ----------- ----------- ----------- -----------
1           1           NULL        NULL        2           3
2           3           1           1           3           8
3           8           2           3           4           19
4           19          3           8           5           67
5           67          4           19          6           45
6           45          5           67          NULL        NULL

在 SQL Server 中,我更喜欢将子查询设为公用表表达式。如果有很多嵌套,这会使代码看起来更线性、嵌套更少并且更容易遵循(此外,在某些重新连接时需要更少的重复)。

【讨论】:

【参考方案3】:

首先,这应该有效(ORDER BY 很重要):

select min(a)
from theTable
where a > 8

select max(a)
from theTable
where a < 8

关于我求你问的第二个问题……:

    select * 
    from theTable
    where date = 8

    union all

    select *
    from theTable
    where key = (select min(key) 
                 from theTable
                 where key > (select max(key)
                              from theTable
                              where date = 8)
                )

    union all

    select *
    from theTable
    where key = (select max(key) 
                 from theTable
                 where key < (select min(key)
                              from theTable
                              where date = 8)
                )

    order by key

【讨论】:

【参考方案4】:
SELECT 'next' AS direction, MIN(date_field) AS date_key
FROM table_name
  WHERE date_field > current_date
GROUP BY 1 -- necessity for group by varies from DBMS to DBMS in this context
UNION
SELECT 'prev' AS direction, MAX(date_field) AS date_key
  FROM table_name
  WHERE date_field < current_date
GROUP BY 1
ORDER BY 1 DESC;

生产:

direction  date_key
---------  --------
prev              3
next             19

【讨论】:

【参考方案5】:

我自己根据 TheSoftwareJedi 尝试设置的解决方案。

第一个问题:

select date from test where date = 8
union all
select max(date) from test where date < 8
union all
select min(date) from test where date > 8
order by date;

第二个问题:

在调试时,我使用了数据集:

(key:date) 1:1,2:3,3:8,4:8,5:19,10:19,11:67,15:45,16:8,17:3,18:1

给出这个结果:

select * from test2 where date = 8
union all
select * from (select * from test2
                   where date = (select max(date) from test2 
                                     where date < 8)) 
    where key = (select max(key) from test2 
                    where date = (select max(date) from test2 
                                      where date < 8))
union all
select * from (select * from test2
                   where date = (select min(date) from test2 
                                     where date > 8)) 
    where key = (select min(key) from test2 
                    where date = (select min(date) from test2 
                                      where date > 8))
order by date,key;

在这两种情况下,最终的 order by 子句严格来说都是可选的。

【讨论】:

【参考方案6】:

如果您的 RDBMS 支持 LAG 和 LEAD,这很简单(Oracle、PostgreSQL、SQL Server 2012)

这些允许在单个查询中选择任何给定行的任一侧的行

【讨论】:

【参考方案7】:

试试这个...

SELECT TOP 3 * FROM YourTable
WHERE Col >= (SELECT MAX(Col) FROM YourTable b WHERE Col < @Parameter)
ORDER BY Col

【讨论】:

这在序列开始时似乎不起作用 - 当 1 是参数值时不返回任何结果。不过最终它确实为 67 工作。

以上是关于表中“下一个”和“上一个”的 SQL 是啥?的主要内容,如果未能解决你的问题,请参考以下文章

从sql列中拆分数据并将其保存在sql存储过程中的另一个表中的最有效方法是啥[重复]

SQL语言中修改表中数据的命令是啥

sql中的select是啥意思

SQL中主键和外键的定义是啥???

SQL语言中修改表中数据的命令是啥

从具有特定根的 SQL 表中获取最新分支的最有效方法是啥?