[英]SQL to find the first occurrence of sets of data in a table
假設我有一張桌子:
CREATE TABLE T
(
TableDTM TIMESTAMP NOT NULL,
Code INT NOT NULL
);
我插入一些行:
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:00:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:10:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:20:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:30:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:40:00', 0);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:50:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:00:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:10:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:20:00', 0);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:30:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:40:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:50:00', 3);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 12:00:00', 3);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 12:10:00', 3);
所以我最終得到了一個類似於以下的表:
2011-01-13 10:00:00, 5
2011-01-13 10:10:00, 5
2011-01-13 10:20:00, 5
2011-01-13 10:30:00, 5
2011-01-13 10:40:00, 0
2011-01-13 10:50:00, 1
2011-01-13 11:00:00, 1
2011-01-13 11:10:00, 1
2011-01-13 11:20:00, 0
2011-01-13 11:30:00, 5
2011-01-13 11:40:00, 5
2011-01-13 11:50:00, 3
2011-01-13 12:00:00, 3
2011-01-13 12:10:00, 3
我怎樣才能 select 每組相同數字的第一個日期,所以我最終得到這個:
2011-01-13 10:00:00, 5
2011-01-13 10:40:00, 0
2011-01-13 10:50:00, 1
2011-01-13 11:20:00, 0
2011-01-13 11:30:00, 5
2011-01-13 11:50:00, 3
在一天的大部分時間里,我一直在處理子查詢之類的問題,但出於某種原因,我似乎無法破解它。 我敢肯定在某個地方有一個簡單的方法!
我可能想從結果中排除 0,但現在這並不重要。
我確定在某個地方有一個簡單的方法!
就在這里。 但首先是兩個問題。
該表不是關系數據庫表。 它沒有唯一的密鑰,這是RM和規范化所要求的(具體地說,每一行必須具有唯一的標識符;不一定是PK)。 因此,SQL(一種用於在關系數據庫表上操作的標准語言)無法對其執行基本操作。
所以問題實際上是SQL在非關系堆中找到第一組數據集 。
現在,如果你的問題是SQL來查找Relational表中第一次出現的數據集,當然暗示一些唯一的行標識符,這將是(a)在SQL中很容易,以及(b)快速的任何SQL的風格。 。
這個問題非常通用(沒有投訴)。 但是,這些特定需求中的許多通常在更大的上下文中應用,並且上下文具有本說明書中不存在的要求。 通常需要一個簡單的子查詢(但在Oracle中使用物化視圖來避免子查詢)。 子查詢也取決於外部上下文,外部查詢。 因此,小通用問題的答案將不包含實際特定需求的答案。
無論如何,我不想回避這個問題。 為什么我們不使用現實世界的例子,而不是簡單的通用例子; 並在Relational表中查找另一組數據中的一組數據的第一個或最后一個或最小值或最大值 ?
主要查詢
讓我們使用上一個問題中的▶數據模型◀ 。
報告自特定日期以來的所有Alerts
,其持續時間的峰值為未Acknowledged
由於您將使用完全相同的技術(具有不同的表和列名稱)來滿足您的所有時間和歷史要求,因此您需要完全理解子查詢的基本構造及其不同的應用程序。
請注意,您不僅擁有純5NF數據庫和關系標識符(復合鍵),而且您具有完整的Temporal功能,並且在不破壞5NF(無更新異常)的情況下呈現時間要求,這意味着
ValidToDateTime
和持續時間的ValidToDateTime
是派生的,而不是在數據中重復。 點是,這使事情變得復雜,因此這 不是子查詢教程的最佳示例 。
首先根據您需要的結果集的結構 ,使用最小連接等構建外部查詢,僅此而已。 首先解析外部查詢的結構是非常重要的; 否則你會來回試圖使子查詢適合外部查詢,反之亦然。
Alerts
需要▶SQL代碼◀是第1頁(抱歉,SO編輯功能很糟糕,它會破壞格式化,代碼已經格式化)。
然后構建子查詢以填充每個單元格。
子查詢(1)導出 Alert.Value
這是一個簡單的派生數據,從生成Alert
的Reading
中選擇Value
。 這些表是相關的,基數是1 :: 1,所以它是PK上的直接連接。
需要▶SQL代碼◀是第2頁。
我有意在外部查詢中添加了多個連接,並通過子查詢獲取數據,這樣您就可以學習(您可以通過連接交替獲取Alert.Value
,但這會更加麻煩 )。
我們需要的下一個子查詢派生Alert.PeakValue
。 為此,我們需要確定Alert
的時間持續時間。 我們有Alert
持續時間的開始; 我們需要確定持續時間的結束,這是范圍內的下一個 (暫時) Reading.Value
。 這也需要一個Subquery,我們最好先處理它。
子查詢(2)導出 Alert.EndDtm
稍微復雜Suquery以選擇第一Reading.ReadingDtm
,即大於或等於Alert.ReadingDtm
,具有Reading.Value
小於或等於其Sensor.UpperLimit
。
處理5NF時態數據
為了處理5NF數據庫中的時間要求(其中未存儲EndDateTime
,如同重復數據),我們僅處理StartDateTime
,並導出 EndDateTime
:它是下一個 StartDateTime
。 這是持續時間的時間概念。
EndDateTime
並將其報告為Next.StartDateTime
,並忽略一毫秒的問題。 This.StartDateTime
和< Next.StartDateTime
。
Sensor.UpperLimit
(即監視它,因為它們通常都位於一個WHERE
子句中,很容易將它們混淆或混淆)。 所需的▶SQL代碼◀以及使用的測試數據在第3頁。
子查詢(3)導出 Alert.PeakValue
現在很容易。 從Alert.ReadingDtm
和Alert.EndDtm
之間的Readings
選擇MAX(Value)
,即Alert
的持續時間。
需要▶SQL代碼◀是第4頁。
標量子查詢
除了是相關子查詢之外,以上都是標量子查詢 ,因為它們返回單個值; 網格中的每個單元格只能填充一個值。 (返回多個值的非標量子查詢非常合法,但不適用於上述情況。)
子查詢(4)已確認的警報
好了,現在你已經掌握了上面的相關標量子查詢,那些填充集合中單元格的子集,一個由外部查詢定義的集合,讓我們看一下可以用來約束外部查詢的子查詢。 我們並不真正想要所有 Alerts
(上圖),我們需要Un-Acknowledged Alerts
: Alert
中存在的標識符,在Acknowledgement
不存在。 那不是填充單元格,即改變外部集合的內容 。 當然,這意味着更改WHERE
子句。
FROM
和現有 WHERE
子句沒有變化。 只需添加WHERE
條件即可排除已Acknowledged Alerts
。 1 :: 1基數,直相關聯接。
需要▶SQL代碼◀是第5頁。
不同的是,這是一個非標量子查詢 ,產生一組行(一列)。 我們有一整套Alerts
(外部集)與一整套Acknowledgements
相匹配。
1
,因為我們正在執行存在檢查。 將其可視化為添加到外部查詢定義的Alert
集上的列。 WHERE NOT IN ()
,但同樣,構造定義的列集,然后比較兩個集。 慢得多。 子查詢(5) Actioned Alerts
作為外部查詢的替代約束,對於未執行的Alerts
,而不是(4),排除一組Actioned Alerts
。 直接相關聯接。
需要▶SQL代碼◀是第5頁。
此代碼已在Sybase ASE 15.0.3上使用1000個Alerts
和200個已Acknowledgements
的不同組合進行了測試; 以及文件中確定的Readings
和Alerts
。 所有執行的零毫秒執行時間(0.003秒分辨率)。
(6) ▶從閱讀◀注冊提醒
此代碼在循環(提供)中執行,選擇超出范圍的新Readings
,並創建Alerts
,除非適用的Alerts
已存在。
(7) ▶從閱讀◀加載警報
鑒於您有一整套用於Reading
的測試數據,此代碼使用修改后的(6)形式加載適用的Alerts
。
當你知道如何時,它是“簡單的”。 我再說一遍,編寫沒有編寫子查詢能力的SQL是非常有限的; 它對於處理關系數據庫至關重要,這是SQL的設計目標。
我想你可以找出你剩下的查詢。
注意,這個例子也恰好證明了使用關系標識符的能力 ,因為我們想要的幾個表之間不必連接(是的!事實是關系標識符意味着更少,而不是更多,連接,而不是Id
鍵)。 只需按照實線。
DateTime
鍵。 想象一下嘗試用Id
PKs編寫上面的代碼,會有兩個級別的處理:一個用於連接(並且會有更多的連接),另一個用於數據處理。 我試圖遠離口語標簽(“嵌套”,“內部”等)因為它們不具體,並堅持特定的技術術語。 為了完整和理解:
FROM
子句之后的子查詢,是一個物化視圖 ,一個查詢中派生的結果集,然后作為“表”輸入另一個查詢的FROM
子句。
WHERE
子句中的子查詢是謂詞子查詢 ,因為它更改了結果集的內容(它所基於的內容)。 它可以返回標量(一個值)或非標量(多個值)。
對於Scalars,請使用WHERE column =
或任何標量運算符
對於非Scalars,使用WHERE [NOT] EXISTS
或WHERE column [NOT] IN
WHERE
子句中的Suquery 不需要相關; 以下工作正常。 識別所有多余的附屬物:
SELECT [Never] = FirstName, [Acted] = LastName FROM User WHERE UserId NOT IN ( SELECT DISTINCT UserId FROM Action )
PostgreSQL支持窗口函數,看看這個
[編輯]嘗試以下方法:
SELECT TableDTM, Code FROM
(
SELECT TableDTM,
Code,
LAG(Code, 1, NULL) OVER (ORDER BY TableDTM) AS PrevCode
FROM T
)
WHERE PrevCode<>Code OR PrevCode IS NULL;
試試這個:
SELECT MIN(TableDTM) TableDTM, Code
FROM
(
SELECT T1.TableDTM, T1.Code, MIN(T2.TableDTM) XTableDTM
FROM T T1
LEFT JOIN T T2
ON T1.TableDTM <= T2.TableDTM
AND T1.Code <> T2.Code
GROUP BY T1.TableDTM, T1.Code
) X
GROUP BY XTableDTM, Code
ORDER BY 1;
你可以嘗試一下嗎?
"SELECT DISTINCT Code, (SELECT MIN(TableDTM) FROM T AS Q WHERE Q.Code = T.Code) As TableDTM FROM T;"
如果您需要排除0,請將其更改為:
SELECT DISTINCT Code, (SELECT MIN(TableDTM) FROM T AS Q WHERE Q.Code = T.Code) As TableDTM FROM T WHERE Code <> 0;
也許我不明白這個問題。 但我沒有看到任何關於公用表表達式或分析函數的提及。 這些是我解決大多數問題的首選武器,當它們無法處理時,我開始求助於臨時表。
我想,我最近解決了一個類似的問題,在處理一個日常的接口文件時,想獲取第一次出錯的數據。 接口上出現問題的記錄被移除到一組保持表中,以便處理rest條記錄。
-- EE with errors removed from most recent batch
with current_batch as (
select employee_number, PVL.ADDITIONAL_INFORMATION
from PERSONNEL_VALIDATION_LOG_X PVL
where PVL.PERSONNEL_BATCH_ID = EMPSRV.CURRENTPERSONNELBATCH(6,900)
)
, hist as (
select
row_number() over (
partition by X.EMPLOYEE_NUMBER, X.ADDITIONAL_INFORMATION
order by B.BATCH_STATUS_DATE
) as RN,
B.PERSONNEL_BATCH_ID BatchId,
B.SUBMITTAL_DATE,
X.EMPLOYEE_NUMBER EMPNUM,
MX.LAST_NAME,
MX.FIRST_NAME,
X.ADDITIONAL_INFORMATION
from PERSONNEL_VALIDATION_LOG_X X
join current_batch C on
X.Employee_number = C.EMPLOYEE_NUMBER
and X.additional_information = C.ADDITIONAL_INFORMATION
join empsrv.personnel_batch B
on B.PERSONNEL_BATCH_ID = X.PERSONNEL_BATCH_ID
join EMPSRV.PERSONNEL_MEMBER_DATA_X MX
on X.PERSONNEL_BATCH_ID = MX.PERSONNEL_BATCH_ID
and X.EMPLOYEE_NUMBER = MX.EMPLOYEE_NUMBER
)
select
batchId,
to_char(submittal_date, 'mm/dd/yyyy') First_Reported,
EmpNum,
Last_name,
first_name,
additional_information
from hist where rn = 1
order by submittal_date desc;
第一個 CTE 只是將總體限制為當前錯誤。 hist CTE 遍歷日志並找出該錯誤的第一次出現(即 ame EE 和 messge)這並不完美,因為錯誤可能消失並返回,我會得到最舊的出現而不是開始最近的序列。 但這已經足夠好了,而且不太可能是由於錯誤消息本身的形狀。 finally 查詢只是選擇每個組的第一行,這將是第一次出現。
查詢需要幾秒鍾才能運行,但我的日志不是特別大,所以性能對我來說幾乎從來都不是問題。 我也不太注意問題的日期。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.