簡體   English   中英

SQL 日期查詢 - 此條件成立多長時間

[英]SQL Dates Query - how long has this condition been true

問題是這些客戶在任何給定日期都是混蛋多久。

我正在對抗 Sybase

對於這個表history_data的簡化表結構

table: history_of_jerkiness
processing_date  name  is_jerk
---------------  ----- -------
20090101         Matt  true
20090101         Bob   false        
20090101         Alex  true        
20090101         Carol true        
20090102         Matt  true        
20090102         Bob   true        
20090102         Alex  false        
20090102         Carol true        
20090103         Matt  true        
20090103         Bob   true        
20090103         Alex  true        
20090103         Carol false        

第 3 次的報告應該顯示,馬特一直是個混蛋,亞歷克斯剛剛成為一個混蛋,鮑勃已經混蛋了 2 天。

name    days jerky
-----   ----------
Matt    3
Bob     2
Alex    1

我想動態地找到這些時間跨度,所以如果我第二次運行報告,我應該得到不同的結果:

name    days_jerky
-----   ----------
Matt    2
Bob     1
Carol   2

這里的關鍵是試圖只找到比某個日期更早的連續跨度。 我找到了一些線索,但這似乎是一個有非常聰明的棘手解決方案的問題。

我來自 SQL 服務器的解決方案 - 與 Dems 相同,但我自己設置了一個最小基線。 它假設沒有間隙——也就是說,每個人每天都有一個條目。 如果那不是真的,那么我將不得不循環。

DECLARE @run_date datetime
DECLARE @min_date datetime

SET @run_date = {d '2009-01-03'}

-- get day before any entries in the table to use as a false baseline date
SELECT @min_date = DATEADD(day, -1, MIN(processing_date)) FROM history_of_jerkiness

-- get last not a jerk date for each name that is before or on the run date
-- the difference in days between the run date and the last not a jerk date is the number of days as a jerk
SELECT [name], DATEDIFF(day, MAX(processing_date), @run_date)
FROM (
     SELECT processing_date, [name], is_jerk
     FROM history_of_jerkiness
     UNION ALL
     SELECT DISTINCT @min_date, [name], 0
     FROM history_of_jerkiness ) as data
WHERE is_jerk = 0
  AND processing_date <= @run_date
GROUP BY [name]
HAVING DATEDIFF(day, MAX(processing_date), @run_date) > 0

我使用以下內容創建了測試表:

CREATE TABLE history_of_jerkiness (processing_date datetime, [name] varchar(20), is_jerk bit)

INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Matt', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Bob', 0)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Alex', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Carol', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Matt', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Bob', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Alex', 0)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Carol', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Matt', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Bob', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Alex', 1)
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Carol', 0) 

“如果您構建數據以滿足以下標准,這可以變得簡單......

所有人都必須有一個他們不是混蛋的初始記錄”

數據應該和不應該滿足的標准取決於用戶,而不是開發人員。

如果您構建數據以滿足以下標准,這可以變得簡單......

所有的人都必須有一個他們不是混蛋的初始記錄

你可以做類似...

SELECT
   name,
   MAX(date)   last_day_jerk_free
FROM
   jerkiness AS [data]
WHERE
   jerk = 'false'
   AND date <= 'a date'
GROUP BY
   name

您已經知道基准日期是什么(“日期”),現在您知道他們不是混蛋的最后一天。 我不知道 sybase,但我確信您可以使用一些命令來獲取“a data”和“last_day_jerk_free”之間的天數

編輯:

有多種方法可以人為地創建初始化的“非生澀”記錄。 Will Rickards 建議使用包含聯合的子查詢。 然而,這樣做有兩個不利方面......
1. 子查詢屏蔽了任何可能被使用的索引
2. 假設所有人都有從同一點開始的數據

或者,采用 Will Rickard 的建議並將聚合從外部查詢移動到內部查詢(從而最大限度地利用索引),並與通用的第二個子查詢聯合以創建起始 jerky = false 記錄......

SELECT name, DATEDIFF(day, MAX(processing_date), @run_date) AS days_jerky
FROM (

    SELECT name, MAX(processing_date) as processing_date
    FROM history_of_jerkiness
    WHERE is_jerk = 0 AND processing_date <= @run_date
    GROUP BY name

    UNION

    SELECT name, DATEADD(DAY, -1, MIN(processing_date))
    FROM history_of_jerkiness
    WHERE processing_date <= @run_date
    GROUP BY name

    ) as data
GROUP BY
   name

外部查詢仍然必須在沒有索引的情況下執行最大值,但記錄數量會減少(每個名稱 2 個,而不是每個名稱 n 個)。 由於不要求每個名稱對每個使用日期都有一個值,因此也減少了記錄的數量。 還有很多其他方法可以做到這一點,其中一些可以在我的編輯歷史中看到。

這個怎么樣:

select a.name,count(*) from history_of_jerkiness a
left join history_of_jerkiness b
on a.name = b.name 
and a.processing_date >= b.processing_date
and a.is_jerk = 'true'
where not exists
( select * from history_of_jerkiness c
  where a.name = c.name
  and c.processing_date between a.processing_date and b.processing_date
  and c.is_jerk = 'false'
)
and a.processing_date <= :a_certain_date;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM