【Oracle】lead 和 lag 函數

lead() 和 lag() 函數

Oracle的兩個與偏移量相關的分析函數。

可以獲取到向前或向後偏移指定行的某一列元素。

效果類似於自連接,但使用更加方便,效率更高。

  • lead(field, number, default) over(partition by id order by name)
    當前記錄後 number 條記錄的 field 字段的值,如果沒有默認爲 default

  • lag(field, number, default) over(partition by id order by name)
    當前記錄前 number 條記錄的 field 字段的值,如果沒有默認爲 default

示例一

有 n 條生產線生產 2 中產品(檯燈、手機),每條生產線上有 1 個計數器,每生產一個產品,計數器就會 + 1(計數器最大值爲 999, 即每到 1000 就會清零重新開始)。☆ 假設獲取計數器值的頻率很高,排除瞬間增長 >1000個產品的情況。

  • 構造原始數據
    source

  • 統計每次獲取計數器時數字的增量。
    使用當前記錄的 COUNT 值 減去前一條記錄 的 COUNT 值,如果沒有,默認等於當前記錄,如當前記錄的 COUNT 大於 前一條記錄的 COUNT,則意味着被置零,需要加上 1000

    	/*此段代碼爲測試數據構造 start*/
    	WITH temp AS (
    	SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 500 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 750 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 945 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 130 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 425 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 654 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-01' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 32 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 412 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 521 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 687 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 873 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-02' CNAME, '檯燈' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 952 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 43 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 241 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 434 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 644 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 832 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 5 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-03' CNAME, '手機' ENAME, TO_DATE('2019/09/18 23:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 231 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 496 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 634 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 874 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 969 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 201 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 423 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 654 COUNT FROM dual
    	UNION ALL 
    	SELECT '計數器-04' CNAME, '手機' ENAME, TO_DATE('2019/09/18 22:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 888 COUNT FROM dual
    	)
    	/*此段代碼爲數據構造 end*/
    
    	SELECT R.CNAME,
    	       R.ENAME,
    	       R.CDATE,
    	       R.CURRENT_COUNT,
    	       R.LAST_COUNT,
    	       DECODE(SIGN(R.CURRENT_COUNT - R.LAST_COUNT),
    	              -1,
    	              R.CURRENT_COUNT - R.LAST_COUNT + 1000,
    	              R.CURRENT_COUNT - R.LAST_COUNT) 增量
    	  FROM (SELECT T.CNAME,
    	               T.ENAME,
    	               T.CDATE,
    	               T.COUNT CURRENT_COUNT,
    	               LAG(T.COUNT, 1, T.COUNT) OVER(PARTITION BY T.CNAME, T.ENAME ORDER BY T.CDATE) LAST_COUNT
    	          FROM TEMP T) R;
    	          
    

    result

示例二

有 n 臺檢測器,隔一段時間抓取一次狀態信息,狀態有兩種,正常(normal)和異常(error)。獲取異常時間段。

  • 構造原始數據
    s

  • 統計當前記錄的上一個狀態和下一個狀態。
    假設統計頻率足夠高,忽略狀態轉換之間的之間。
    那麼,要獲取異常區間。
    自身狀態爲 error 並且上一個狀態爲 normal 的時間,爲異常開始時間
    自身狀態爲 error 並且下一個狀態爲 normal 的時間,爲異常結束時間

    /*此段代碼爲測試數據構造 start*/
    WITH temp AS(
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 21:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 21:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 21:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:30:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:40:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 22:50:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 23:00:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 23:10:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'error' STATUS FROM dual
    UNION ALL 
    SELECT '檢測器-01' CNAME, TO_DATE('2019/09/18 23:20:00', 'yyyy/mm/dd hh24:mi:ss') CDATE, 'normal' STATUS FROM dual
    )
    /*此段代碼爲測試數據構造 end*/
    SELECT R.CNAME,
           R.CDATE,
           R.STATUS,
           /* 要獲取異常區間 */
           /*自身狀態爲 error 並且上一個狀態爲 normal 的時間 爲異常開始時間 */
           /*自身狀態爲 error 並且下一個狀態爲 normal 的時間 爲異常結束時間 */
           DECODE(R.STATUS, 'error',DECODE(R.LAST_STATUS, 'normal', R.CDATE, NULL) , NULL ) ERR_START_DATE,
           DECODE(R.STATUS, 'error', DECODE(R.NEXT_STATUS, 'normal', R.CDATE, NULL ), NULL) ERR_END_DATE
            FROM (
    SELECT T.CNAME,
           T.CDATE,
           LAG(T.STATUS, 1, T.STATUS) OVER(PARTITION BY T.CNAME ORDER BY T.CDATE) LAST_STATUS,
           T.STATUS,
           LEAD(T.STATUS, 1, T.STATUS) OVER(PARTITION BY T.CNAME ORDER BY T.CDATE) NEXT_STATUS
      FROM TEMP T ) R;
    
    

    r

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章