寻找范围内的差距或重叠

Finding gaps or overlaps in ranges

假设我有一个 table 格式如下:

|               dbo.ROUTES               |
 ----------------------------------------
| ID | ROUTE | LOWER_LIMIT | UPPER_LIMIT |
 ----------------------------------------
|  0 |   A   |       0     |     10      |
|  1 |   B   |      11     |    500      |
|  2 |   C   |     600     |   1000      |

我如何找到路由条目未涵盖的任何号码范围?即对于上面的示例,我需要能够看到没有涵盖 501 - 599 的条目。

我们目前正在使用此布局,尽管有 4 或 5 个其他列具有不同的标准,并且我们发现(如您所料)随着 table 的增长,下限和上限阈值已更新,我们开始看到空白和重叠。

我知道这确实归结为糟糕的设计,但在我们拥有改进它的资源之前,我们可以在此期间做一些至少可以帮助我们手动整理 table 的事情。

谢谢,

这应该显示两边有空隙的行:

SELECT * FROM ROUTES 
WHERE NOT Exists(SELECT ID FROM ROUTES as sub
 WHERE sub.Lower_Limit = ROUTES.Upper_Limit + 1)
OR NOT Exists(SELECT ID FROM ROUTES as sub1
 WHERE sub1.Upper_Limit = ROUTES.Lower_Limit - 1)

由于您使用的是 SQL Server 2008,因此您无法使用 LEADLAG 函数,您需要自行加入:

SELECT T2.UPPER_LIMIT + 1 AS R1
      ,T1.LOWER_LIMIT - 1 AS R2
FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID) AS RID
            ,ID
            ,ROUTE  
            ,LOWER_LIMIT
            ,UPPER_LIMIT
      FROM ROUTES) AS T1
LEFT JOIN (SELECT ROW_NUMBER() OVER(ORDER BY ID) AS RID
                 ,ID
                ,ROUTE  
                ,LOWER_LIMIT
                ,UPPER_LIMIT
           FROM ROUTES) AS T2
    ON T1.RID = T2.RID + 1
WHERE T1.LOWER_LIMIT - T2.UPPER_LIMIT > 1;

在 SQL 服务器 2012+ 中:

SELECT LAG_UPPER_LIMIT + 1 AS R1
      ,LOWER_LIMIT     - 1 AS R2
FROM (SELECT ID
            ,ROUTE  
            ,LOWER_LIMIT
            ,UPPER_LIMIT
            ,LAG(UPPER_LIMIT) OVER (ORDER BY ID) AS LAG_UPPER_LIMIT
      FROM ROUTES) AS T
WHERE T.LOWER_LIMIT - T.LAG_UPPER_LIMIT > 1

这为您提供了准确的缺失范围。

由于您不能使用 lead/lag 函数,我使用了替代方法来实现这一点。根据需要编辑输出列中的边界条件(missing_val,重叠)adding/subtracting 1

输入:

ID  LOWER_LIMIT UPPER_LIMIT

0   0   10
1   11  500
2   600 1000
3   980 1100

输出:

ID  LOWER_LIMIT UPPER_LIMIT MISSING_VAL OVERLAPPING

0   0   10      0   0
1   11  500 500-600 0
2   600 1000    0   980-1000
3   980 1100    0   0

查询:

SELECT ID, LOWER_LIMIT, UPPER_LIMIT, CASE WHEN 
UPPER_LIMIT+1=NEXT_LOWER_VAL THEN '0' 
WHEN UPPER_LIMIT+1< NEXT_LOWER_VAL THEN
UPPER_LIMIT||'-'||NEXT_LOWER_VAL ELSE '0' END AS MISSING_VAL,
CASE WHEN 
UPPER_LIMIT+1= NEXT_LOWER_VAL THEN '0' 
 WHEN UPPER_LIMIT+1> NEXT_LOWER_VAL THEN NEXT_LOWER_VAL||'-'||UPPER_LIMIT ELSE '0' END AS       OVERLAPPING
FROM 
(
SELECT T1.*, (SELECT MIN(LOWER_LIMIT) FROM TEST_T T WHERE T.ID<> T1.ID AND T.LOWER_LIMIT>     T1.LOWER_LIMIT) AS NEXT_LOWER_VAL
FROM TEST_T T1) SUB

您正在寻找的是不与下一个 lower_limit 值重叠的 upper_limit 值。其实你想要比上限值大一比下一个下限值小一:

select r.upper_limit + 1 as missing_lower,
       (select min(lower_limit) - 1
        from routes r3
        where r3.lower_limit > r.upper_limit + 1
       ) as missing_higher
from routes r
where not exists (select 1
                  from routes r2
                  where r.upper_limit + 1 between r2.lower_limit and r2.upper_limit
                 );