Sql-Server

確定連續出現的值

  • October 4, 2021

我有一個如下所示的表格:

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

我想知道在按順序以及第一個和最後一個CAR NAME順序時,相同的連續插入了多少次。出於此查詢的目的,應忽略一個連續的結果。INSERT DATE``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

我在執行此操作時遇到問題,也許有人可以提供幫助。

這是針對 SQL Server 的。不幸的是,我只有兩列可供使用。

你的問題沒有明確形成,但考慮到你的例子 -菲爾的評論是正確的:

您的範例僅在有另一列定義數據順序(即您在問題中呈現數據的順序)時才有意義。

除非您有一個具有行順序的附加列 - 您的問題沒有解決方案。

為什麼? 因為 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

我很高興弄清楚如何實際查詢以獲得所需的結果,但就是這樣(對不起,我沒有花太多時間進行格式化):

;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

主要思想是找到範圍的第一項和最後一項(使用視窗函式LAGLEAD),然後將第一項與最後一項ROW_NUMBER()作為鍵配對。最後但並非最不重要的一點是將這些對與原始表再次連接以獲得COUNT(*)s。瞧:

有趣的查詢

舊執行緒,但這仍然是一個有趣的問題。

正如Błażej提到的,我們需要一個可以排序的列,沒有關係。否則,查詢將不知道哪個記錄先出現,並且會以某種不可預測的方式將事物計為一個組。

假設我們有這個列(讓我們命名它seq),下面的查詢也可以工作:

WITH grouped_cars AS (
 SELECT name,
        date,
        (
          name,
          -- There is a subtraction below,
          -- don't be fooled by the formatting
          DENSE_RANK() OVER (ORDER BY seq) 
        - DENSE_RANK() OVER (PARTITION BY name ORDER BY seq)
        ) AS car_group
 FROM cars
 )
SELECT MIN(name), -- Could be 'ARBITRARY(name)' in Presto
      COUNT(1) AS count, 
      MIN(date) AS first,
      MAX(date) AS last
FROM grouped_cars
GROUP BY car_group
HAVING COUNT(1) > 1
ORDER BY MIN(date)

這是帶有查詢的 SQLFiddle 的連結:http ://sqlfiddle.com/#!17/10304/5

這個問題真的很好地解釋了這個技巧:用row_number()和dense_rank()解決“差距和島嶼”?

這個怎麼運作?

訣竅是,如果你減去兩個執行序列,你會得到所有元素的相同數字8, 9, 10- 1, 2, 3= 7, 7, 7。但是如果第二個序列亂序8, 9, 10, 11, 12- 1, 2, 3, 1, 2= ,結果會有所不同7, 7, 7, 10, 10

因此,dense_rank()一切的第一個為您提供了第一個執行序列。dense_rank()在汽車名稱上劃分的第二個為您提供了第二個。當其中的數字是連續的時,我們會得到相同的結果,但每次它因為另一個分區交錯而“中斷”時,數字都會改變。

最後,你把車名和數字放在一起,這給了你一些沒有真正意義的東西,但這對於序列上的所有項目都是相等的,沒有間隙。

鑑於此,只需按它分組即可:)

引用自:https://dba.stackexchange.com/questions/210907