【问题标题】:T_SQL query extreme valueT SQL查询极值
【发布时间】:2015-10-16 10:37:25
【问题描述】:

有一个表 T,id 有一个随机值,如何通过一次选择我们可以得到 input 中 id 的极值。 示例:

T.id = 
12
34
76
89
1234
1254
6789
3456

对于输入,我们给出选择 id=1254,作为输出,我们必须得到两个值 1234 和 6789

【问题讨论】:

  • 。 . SQL 表代表 无序 集。如果没有包含此信息的列,您将无法定义下一行或上一行。您还应该使用您正在使用的数据库标记您的问题。

标签: sql sql-server tsql select


【解决方案1】:

你可以这样做:

采样日期

CREATE TABLE #Test (ID INT)

INSERT INTO #Test VALUES (12),(34),(76),(89),(1234),(1254),(6789),(3456)

输入

DECLARE @var INT = 1234

查询

;WITH cte AS
(
SELECT Id, 
       ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) rn1
FROM #Test t
)
SELECT PrevId, NextId
FROM cte
LEFT JOIN (
    SELECT Id PrevId, 
           ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) rn
    FROM #Test t1   
    ) previd ON cte.rn1 = previd.rn +1    
LEFT JOIN (
    SELECT Id NextId, 
           ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) rn
    FROM #Test t1   
    ) nextid ON cte.rn1 = nextid.rn -1
WHERE cte.Id = @var

输出

PrevId  NextId
89      1254

演示

您可以在 SQL FIDDLE

进行测试

【讨论】:

  • 但我们只需要找到正确的极端值 - 6789。对于输入,我们给出选择 id=1254,作为输出,我们必须得到两个值 1234 和 6789
  • 但是对于 1245 它返回 1234 和 6789。怎么了?
  • @Jack Strange,对于 DECLARE @var INT = 1254 它返回 1234 和 6789,我也测试过它。你可以在sqlfiddle.com/#!3/162b5/1查看它
  • 你做了很大的假设,ORDER BY (SELECT NULL) 将返回data in the same unordered set as provided in sample data 。如果查询优化器试图并行化这个过程怎么办?或者尝试使用足够大的数据集,例如 (10k +) 并添加 OPTION (MAXDOP 4)。它可能会像纸牌屋一样失败。
【解决方案2】:

您可以使用条件聚合:

select max(case when id < 1254 then id end) as prev,
       min(case when id > 1254 then id end) as next
from t;

类似的方法会产生两行,但如果您有索引,效率会更高:

select 'prev', max(id)
from t
where id < 1254
union all
select 'next', min(id)
from t
where id > 1254;

编辑:

我似乎错过了 id 乱序。在这种情况下,您需要假设有一列指定数据的顺序。 SQL 表表示 无序 集合,因此没有下一个或上一个值。如果你有一个排序列,你可以使用窗口函数来处理这个问题:

with n as (
      select t.*, row_number() over (order by <ordering column goes here>) as seqnum
      from t
     )
select max(case when seqnum = theseqnum - 1 then id end) as prev_id,
       max(case when seqnum = theseqnum + 1 then id end) as next_id
from (select n.*,
             max(case when id = 1254 then seqnum end) as theseqnum
      from n
     ) n
where seqnum = theseqnum - 1 or seqnum = thesequm + 1;

【讨论】:

  • 它将失败,数据如下:(90),(11),(1254),(2),(5)。仅当先前的值较低且下一个值高于提供的 Id 时,这才有效。
  • OVER (ORDER BY (SELECT NULL)) 怎么样?它将返回与示例数据中提供的相同无序集中的数据,还是我错了?
  • @StanislovasKalašnikovas 。 . . SQL 表是无序的。根据我的经验,over (order by (select null)) 不会进行排序。但是,这并不意味着结果是有序的。在多线程系统上,不同的线程可能会以不同的顺序返回数据。
【解决方案3】:

您没有提及您使用的是哪个 SQL Server 版本。如果是 2012+,您可以使用 LAG()LEAD() 函数来快速实现:

滞后:

访问同一结果集中前一行的数据,不带 在 SQL Server 2012+ 中使用自联接。 LAG 提供对行的访问 在当前行之前的给定物理偏移量处。用这个 SELECT 语句中的分析函数来比较 当前行的值在前一行中。

领导:

访问同一结果集中后续行中的数据,不带 在 SQL Server 2012+ 中使用自联接。 LEAD 提供对行的访问 在当前行之后的给定物理偏移量处。用这个 SELECT 语句中的分析函数来比较 当前行的值在下一行。

这是一个例子:

DECLARE @Test TABLE
(
    ID INT
);

INSERT INTO @Test
VALUES (12)
    , (34)
    , (76)
    , (89)
    , (1234)
    , (1254)
    , (6789)
    , (3456);


;WITH CTE (ID, Prev, Next, Extreme)
AS (
    SELECT ID
        , LAG(ID) OVER (ORDER BY (SELECT NULL))
        , LEAD(ID) OVER (ORDER BY (SELECT NULL))
        , MAX(ID) OVER ()
    FROM @Test
    )
SELECT *
FROM CTE
WHERE ID = 1234;

这个查询返回

╔══════╦══════╦══════╦═════════╗
║  ID  ║ Prev ║ Next ║ Extreme ║
╠══════╬══════╬══════╬═════════╣
║ 1234 ║   89 ║ 1254 ║    6789 ║
╚══════╩══════╩══════╩═════════╝

正如 lad2025 所说,如果 sql server 优化器决定使用并行性,这个查询以及已回答的查询将会中断。

使用OPTION(MAXDOP 1) 提示可能会解决它。它告诉 sql server 不要使用并行性。唯一正确的方法是拥有列,您可以为您的数据集提供稳定的顺序。

【讨论】:

  • 与第一个答案相同:您做出了很大的假设,即 ORDER BY (SELECT NULL) 将返回与示例数据中提供的相同无序集中的数据。如果查询优化器试图并行化这个过程怎么办?或者尝试使用足够大的数据集,例如 (10k +) 并添加 OPTION (MAXDOP 4)。它可能会像纸牌屋一样失败。
  • 我从来不知道这一点。谢谢现场。使用 maxdop 1 会有帮助吗?
  • 它可以防止并行是的,看起来它可能会有所帮助。但是,如果没有ORDER BY 列,您将永远无法 100% 确定。查询优化器是聪明的野兽,里面可以做它想做的事情,它可能会在版本之间改变:)
  • 我想没有其他方法可以让它工作,除非 OP 的数据集有一些给定的顺序,这将保证正确的结果。
  • @lad2025 我已经用你们的 cmets 更新了我的答案。再次感谢您!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-21
  • 2014-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多