确定连续出现的值

Tom*_*Tom -1 sql-server gaps-and-islands

我有一张表,如下所示:

CAR NAME    INSERT DATE
MERCEDES    2018-01-01
SEAT        2018-01-01
MERCEDES    2018-01-02
BMW         2018-01-02
MERCEDES    2018-01-03
MERCEDES    2018-01-04
MERCEDES    2018-01-05
BMW         2018-01-05
BMW         2018-01-06
SEAT        2018-01-07
BMW         2018-01-08
AUDI        2018-01-08
BMW         2018-01-09  
BMW         2018-01-10
NULL        2018-01-12
SEAT        2018-01-12
SEAT        2018-01-14
SEAT        2018-01-16
BMW         2018-01-17
NULL        2018-01-19 
MERCEDES    2018-01-21
MERCEDES    2018-01-22
MERCEDES    2018-01-23
Run Code Online (Sandbox Code Playgroud)

我想知道有多少次连续CAR NAME插入到表中,排序时INSERT DATE,以及 first 和 last INSERT DATE。出于此查询的目的,CAR NAME应忽略一个连续的结果。

例如:

name  count  first     last
mercedes 3 2018-01-03 2018-01-05
bmw      2 2018-01-05 2018-01-06
bmw      2 2018-01-09 2018-01-10
seat     3 2018-01-12 2018-01-16
mercedes 3 2018-01-21 2018-01-23
Run Code Online (Sandbox Code Playgroud)

我在实现这个时遇到了问题,也许有人可以提供帮助。

这是针对 SQL Server 的。不幸的是,我只有两列可供使用。

小智 8

你的问题没有明确形成,但考虑到你的例子 -菲尔的评论是正确的:

只有当有另一列定义数据的顺序(这是您在问题中呈现数据的顺序)时,您的示例才有意义。

除非您有一个附加的行顺序列 - 您的问题没有解决方案。

为什么? 因为 SQL 基于关系理论,在这个概念中数据本身没有顺序。因此,除非您提供具有行顺序的附加列(最常见的是带有递增数字的 Id 列),否则将无法告诉数据的顺序,因此 - 您的问题无法得到解答。如果您SELECT从数据库中执行,如果没有带有订单号的列,SQL 官方不保证您每次都会以相同的顺序接收行(并且在许多情况下您不会)。

解决方案 添加另一列,例如 Id 作为整数,并让每一行都有一个这样的递增值:

Id    NAME         DATE
1   MERCEDES    2018-01-01
2   SEAT        2018-02-01
3   MERCEDES    2018-04-01
4   BMW         2018-01-01
5   MERCEDES    2018-01-01
6   MERCEDES    2018-01-05
7   MERCEDES    2018-01-09
Run Code Online (Sandbox Code Playgroud)

我很高兴弄清楚如何实际查询以获得所需的结果,但在这里(对不起,我没有花太多时间进行格式化):

;WITH First (id, car, d, is_first, rn) AS
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY id) rn FROM (
        SELECT
            id
            ,car
            ,d
            ,CASE WHEN ((LEAD(car,1) OVER (ORDER BY id) = car) AND (LAG(car,1) OVER (ORDER BY id) <> car OR LAG(car,1) OVER (ORDER BY id) IS NULL)) THEN 1 ELSE 0 END is_first
        FROM 
            dbo.cars
    ) t
    WHERE t.is_first = 1
),
Last (id, car, d, is_last, rn) AS
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY id) rn FROM (
        SELECT
            id
            ,car
            ,d
            , CASE 
                WHEN (LEAD(car,1) OVER (ORDER BY id) <> car OR LEAD(car,1) OVER (ORDER BY id) IS NULL) AND (LAG(car,1) OVER (ORDER BY id) = car ) THEN 1 ELSE 0 END is_last
        FROM 
            dbo.cars
    ) t
    WHERE t.is_last = 1
)
SELECT
    c.car, COUNT(*) cnt, f.d min_date, l.d max_date
FROM
    First f
    LEFT JOIN Last l ON f.rn = l.rn
    LEFT JOIN cars c ON c.car = f.car AND c.id BETWEEN f.id AND l.id
GROUP BY 
    c.car, f.d, l.d, f.rn
ORDER BY 
    f.rn
Run Code Online (Sandbox Code Playgroud)

主要思想是找到范围的第一个和最后一个项目(使用窗口函数LAGLEAD),然后将第一个项目与最后一个项目ROW_NUMBER()作为键配对。最后但并非最不重要的一点是再次将这些对与原始表连接起来以获得COUNT(*)s。等等:

有趣的查询

  • 好的。您可能想补充一点,该问题属于“间隙和岛屿”问题。 (2认同)