![](/img/trans.png)
[英]SQL Query: How to check for the Join condition in ON clause only if a condition is true
[英]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.