【问题标题】:Selecting entries with biggest value less than list of values选择最大值小于值列表的条目
【发布时间】:2023-04-06 23:03:02
【问题描述】:

假设我的表结构如下:

id | Word
---|-----
1  | a
2  | aa
.  | ..

我有一个这样的 id 列表:

(...,900, 1000, 2000, 3000, 4000,....)

我想在上面的列表中找到小于每个 id 的最大 id。 我的表id不一定是连续的,两个连续的id之间有一些差距,例如:

(...,889,900,950,952,997,1000,1001,1010,1920,2000,2990,3000,3500,4000,...)

根据上述列表的预期结果是:

(889, 997, 1920, 2990, 3500,...)

我如何达到预期的效果?

【问题讨论】:

  • 只有889小于上面列表中的每个id
  • 这个id列表是从哪里来的?它是表格还是硬编码?您正在搜索的所有这些 id 是否存在,或者它们可能会丢失一些?
  • 不幸的是,它们是硬编码的。 AFAIK,一切都存在
  • 现在我看到了接受的答案,我理解了这个问题。您应该有一些样本之间的值超过一个。如果两者之间没有,该怎么办。

标签: sql sql-server tsql groupwise-maximum


【解决方案1】:

使用common table expressionROW_NUMBER()

;WITH cte AS(
    SELECT *, ROW_NUMBER() OVER (ORDER BY ID) rowNum
    FROM example)

SELECT ID, word
FROM cte
WHERE rowNum IN (
    SELECT (rowNum - 1)
    FROM cte
    WHERE ID IN ('900','1000','2000','3000','4000'))
    --WHERE ID IN (SELECT ID FROM <tableWithIDs>))


如果您已经在另一个表中找到了所有 ID,则可以改用我的答案的注释部分,而不是硬编码的 IN 列表。

仅当您要查找的ID 存在于表中时,这才有效。因此,如以下评论中所述,如果您正在搜索 1001,则不会得到 997,除非表中存在 1001(意思是,如果存在,它将获得 rowNum 值并且可以使用在子查询中递减)

[DEMO HERE]


以下是另一种查看前一个ID 的方法:

SELECT *, LEAD(ID,1) OVER(ORDER BY ID DESC) PreviousID
FROM example
ORDER BY ID

【讨论】:

  • 我的 id 列表远大于问题正文中出现的列表。
  • @m.r226 好的,给我几分钟,我会给你一个更健壮的方法
  • @m.r226 - 我已经发布了一个更强大/更动态的方法来解决这个问题。
  • 如果在 WHERE 中将 1000 更改为 1001 会怎样?
  • @dnoeth - 我听到了,997 不会出现,因为1001 在我的测试表中不存在。我为 OP 添加了另一种方式来查看他们可能正在寻找的数据。根据我的测试,Gordon 的答案似乎不需要 ID 存在
【解决方案2】:

我会这样做:

select v.val, t.*
from (values (900), (1000), (2000), (3000), (4000) ) v(val) outer apply
     (select top 1 t.*
      from t
      where t.id < v.val
      order by t.id desc
     ) t;

这允许您查看每一行的值。这可能很重要,因为 SQL 结果集是无序的,并且哪个值与哪个行对应并不明显。

编辑:

如果您知道表中的行号,那么性能最高的解决方案可能是:

select t.*
from (select t.*, lead(id) over (order by id) as next_id
      from t
     ) t
where next_id in ( . . . );

【讨论】:

  • @GordonLinoff- 您的答案不需要寻找ID 在表格中,这确实使您的答案成为未来访问者最适用的解决方案(如果他们的ID 不是表中)。 “我只会这样做:...” 让我有点笑了.. 我有目标了!
  • @Brien 。 . .您的评论让我意识到,如果您知道 id 在表格中,那么还有一个性能更高的解决方案。
  • @GordonLinoff- 酷!是的,我有点担心提到LEAD(),尽管在我之前的编辑中是在不同的上下文中,因为当我发现他们没有使用 SQL Server 2012+ 时,我已经被其他用户甚至 OP 的地狱之火咬了.
【解决方案3】:

这应该可行,我认为它会相当有效。

declare @V table (num int primary key);
insert into @V values (800), (889), (900), (997), (1000), (1910), (1920), (2000), (2990), (3000), (3500), (4000);
declare @T table (num int primary key);
insert into @T values (800), (900), (1000), (1200), (2000), (3000), (4000);
select tt.vP 
  from ( select t.num as t, v.num as v
              , LAG(v.num) over (order by v.num) as vP
           from @V v
           left join  @T t
             on v.num = t.num 
       ) tt 
 where tt.t  is not null 
   and tt.vP is not null
 order by tt.vP

不清楚你希望它如何表现

select t.num 
     , (select max(v.num) from @V v where v.num < t.num) as prior
from @T t

【讨论】:

  • 如果你将@T 中的 1000 改为 1001 会怎样?
  • @dnoeth OP 说一切都存在,但没有说如果不存在该怎么办。
猜你喜欢
  • 2016-10-03
  • 2019-05-09
  • 2018-08-25
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 2020-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多