SQL在特定行周围选择“窗口”

Posted

技术标签:

【中文标题】SQL在特定行周围选择“窗口”【英文标题】:SQL Selecting "Window" Around Particular Row 【发布时间】:2008-12-29 16:54:52 【问题描述】:

以前很可能有人问过这样的问题,但我想不出要搜索的字词。

我正在开发一个照片库应用程序,并希望显示 9 个缩略图来显示当前照片的上下文(在 3x3 网格中,当前照片位于中心,除非当前照片位于前 4正在显示的照片,在这种情况下,例如,如果当前照片是第二张我想选择照片 1 到 9)。例如,给定一个包含带有 id 的照片列表的相册:

1、5、9、12、13、18、19、20、21、22、23、25、26

如果当前照片是 19 岁,我还想查看:

9、12、13、18、19、20、21、22、23

如果当前照片是5,我还想查看:

1、5、9、12、13、18、19、20、21

我一直在想一些类似的事情:

SELECT *
FROM photos
WHERE ABS(id - currentphoto) < 5
ORDER BY id ASC 
LIMIT 25

但这不适用于 id 不连续的情况(如上面的示例),或者当前照片之前的照片不足的情况。

有什么想法吗?

谢谢,

Dom

附言如果有任何不清楚的地方,请发表评论,我会澄清这个问题。如果有人能想到一个更有用的标题来帮助其他人将来找到这个问题,那么也请发表评论。

【问题讨论】:

【参考方案1】:

可能只使用一个 UNION,然后在显示结果的程序代码中修剪掉额外的结果(因为这将在非边缘情况下返回 20 行):

(SELECT 
     * 
FROM photos
   WHERE ID < #current_id#
   ORDER BY ID DESC LIMIT 10)
UNION
  (SELECT *
   FROM photos
   WHERE ID >= #current_id#
   ORDER BY ID ASC LIMIT 10)
ORDER BY ID ASC

编辑:根据 le dorfier 的建议,将 UNION 两侧的限制增加到 10。

编辑 2:根据 Dominic 的建议进行修改以更好地反映最终实现。

【讨论】:

这是一个很好的、有用的技术。我过去曾将它与 mysql 一起使用。要处理列表的末尾,您将没有足够的行:使用 LIMIT 10 并在程序代码中计算出选择。 感谢 le dorfier,解决了限制问题,我会按照建议进行编辑。 我最终做了与此非常相似的事情。我在 UNION 的结果上添加了“ORDER BY id ASC”,因此行按预期返回。您需要将第一个 ORDER 更改为 id DESC,否则前几行将始终返回。【参考方案2】:

如果您使用的是 SQL Server,则可以使用 row_number() 函数为您提供行序索引并执行以下操作:

declare @selected_photo integer;
set @selected_photo = 5;

declare @buffer_size integer;
set @buffer_size = 2;

select
   ph.rownum,
   ph.id
from
   (select row_number() over (order by Id) as rownum, * from Photos) as ph
where
   ph.rownum between case
                         when @selected_photo - @buffer_size < 1 then 1
                         else @selected_photo - @buffer_size
                      end
                      and @selected_photo + @buffer_size

编辑: 这是一篇关于在 MySQL 中模拟 row_number() 函数的文章,将其与 这可能会为您提供所需的东西-我会尝试一下,但在工作中没有方便使用的 MySQL 数据库。 :-)

http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/

【讨论】:

谢谢 Ron - 但我使用的是 MySQL,所以我认为我不能使用它 - 可以吗? 我在 MySQL 文档中没有看到任何类似的东西......但我会继续探索。 :-) @RonSavage:别打扰。 MySQL 是少数不支持窗口函数的 DBMS 之一。【参考方案3】:

这是标准的“行排序”问题...如果您的数据库具有 rowId 功能,您可以使用它,否则您需要一个子查询来计算 ID 小于当前行 ID 的行数...比如这个:

-- 假设@Id 是“中间”的 id 值

 Select *  From Photos P
 Where (Select Count(*) From Photos
         Where id <= P.Id)
     Between (Select Count(*) From Photos
              Where id < @Id) - 4
        And  (Select Count(*) From Photos
              Where id < @Id) + 4

当评论提出专辑问题时,您希望将专辑谓词添加到每个子查询

   Select *  From Photos P
   Where (Select Count(*) From Photos
          Where album = @album
            And id <= P.Id)
     Between (Select Case When Count(*) < 4 
                      Then 4 Else Count(*) End
              From Photos
              Where album = @album
                 And id < @Id) - 4
        And  (Select Case When Count(*) < 4 
                      Then 4 Else Count(*) End
              From Photos
              Where album = @album
                  And id < @Id) + 4

【讨论】:

按专辑应该有额外的过滤器,否则不能解决问题 - 即它只适用于顺序 id。

以上是关于SQL在特定行周围选择“窗口”的主要内容,如果未能解决你的问题,请参考以下文章

使用 ROW_NUMBER() 窗口函数选择行

SQL:在 MySQL/MariaDB 中选择具有窗口函数的某些行

如何根据滚动的 30 天窗口 SQL 选择行

如何使用窗口函数检查特定范围值是不是填充在 SQL 中?

在 SQL/Spark 中使用窗口函数执行特定过滤器

调暗对话框周围的屏幕