用sql對含有時間段欄位(起始時間、結束時間)的記錄做並集處理

来源:https://www.cnblogs.com/yongestcat/archive/2020/03/29/12590154.html
-Advertisement-
Play Games

來自於一個基友的問題:他的博客同問題鏈接 sql時間段取並集、合併 https://blog.csdn.net/Seandba/article/details/105152412 問題:計算通道的總開放時長,只要有任意一個終端開放通道就算開放,難點在於各種終端開放時間重疊包含問題測試數據--問題一、... ...


來自於一個基友的問題:

他的博客同問題鏈接    sql時間段取並集、合併 https://blog.csdn.net/Seandba/article/details/105152412 

計算通道的總開放時長,只要有任意一個終端開放通道就算開放,難點在於各種終端開放時間重疊包含

aa128bd9b772d921814e068c67d5e7f4

問題測試數據:

--問題一、測試數據--計算總開放時長(小時)
TRUNCATE TABLE xcp;
insert into xcp values('1','A1',to_date('20200317 01:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 06:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('2','A1',to_date('20200317 01:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 06:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('2','A1',to_date('20200317 01:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 08:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('2','A1',to_date('20200317 02:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 07:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('2','A1',to_date('20200317 03:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 07:00:00','yyyymmdd hh24:mi:ss'));

insert into xcp values('2','A1',to_date('20200317 05:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 09:00:00','yyyymmdd hh24:mi:ss '));
insert into xcp values('3','A1',to_date('20200317 09:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 11:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('3','A1',to_date('20200317 12:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 13:00:00','yyyymmdd hh24:mi:ss'));

insert into xcp values('2','A1',to_date('20200317 14:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 19:00:00','yyyymmdd hh24:mi:ss '));
insert into xcp values('3','A1',to_date('20200317 16:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 19:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('3','A1',to_date('20200317 18:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 19:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('3','A1',to_date('20200317 18:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 21:00:00','yyyymmdd hh24:mi:ss'));
commit;

SELECT * FROM xcp;

image2問題核心是求多條記錄之間的並集操作 ,我寫的sql如下,

--問題1
WITH tmp1 AS (  --取所有時間節點
SELECT channel,BEGIN_TIME TIME FROM xcp
UNION SELECT channel,end_time FROM xcp
UNION SELECT channel,MIN(begin_time) FROM xcp GROUP BY channel
UNION SELECT channel,MAX(end_time) FROM xcp GROUP BY channel),

tmp2 AS(--每個時間節點連接到下個節點  形成時間段
SELECT a.channel,a.time,LEAD(a.time,1) OVER(PARTITION BY a.channel ORDER BY a.time) nexttime
FROM tmp1 a),

tmp3 AS(--每個時間段取中值
SELECT b.channel,b.TIME,b.nexttime,(b.nexttime-b.time)/2+b.time midtime
FROM tmp2 b
WHERE b.nexttime IS NOT NULL),

tmp4 AS(--若中值處於原始記錄中  則該段時間為通道開通時間 否則通道不開通
SELECT c.*,
CASE WHEN EXISTS (SELECT 1 FROM xcp o WHERE c.midtime BETWEEN o.begin_time AND o.end_time) THEN 1 ELSE 0 END *
(c.nexttime-c.time)*24 duration
FROM tmp3 c)

SELECT nvl(d.channel,'合計時長') 通道,d.TIME 開始時間,d.nexttime 結束時間,
SUM(duration) "通道開通時間(小時)" FROM tmp4 d
GROUP BY rollup((d.channel,d.TIME,d.nexttime))
ORDER BY 2;

09e02bd2c2d7e12249c24dc5380b754看著就很垃圾的sql,執行計劃一定垃圾,記錄以備後查詢吧

原理是吧時間節點拿出來,對沒兩個時間節點之間的時間段,取中間值到原始記錄表查詢,如果是,這段時間就是屬於並集後的,然後對並集後的記錄求和


問題2:求17日的的通道開放時長

--問題2、測試數據--計算27號開放時長(小時)
TRUNCATE TABLE xcp;
insert into xcp values('13','A1',to_date('20200314 08:00:00','yyyymmdd hh24:mi:ss'),to_date('20200315 09:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('14','A1',to_date('20200317 08:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 09:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('15','A1',to_date('20200316 03:00:00','yyyymmdd hh24:mi:ss'),to_date('20200317 05:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('16','A1',to_date('20200317 08:00:00','yyyymmdd hh24:mi:ss'),to_date('20200318 10:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('17','A1',to_date('20200316 08:00:00','yyyymmdd hh24:mi:ss'),to_date('20200318 10:00:00','yyyymmdd hh24:mi:ss'));
insert into xcp values('18','A1',to_date('20200320 08:00:00','yyyymmdd hh24:mi:ss'),to_date('20200321 10:00:00','yyyymmdd hh24:mi:ss'));
commit;

SELECT * FROM xcp ORDER BY begin_time

image5sql如下:

----問題2
WITH tmp1 AS (  --取所有時間節點    取17號就加入17號0點和24點兩個時間
SELECT channel,BEGIN_TIME TIME FROM xcp
UNION SELECT channel,end_time FROM xcp
UNION SELECT channel,MIN(begin_time) FROM xcp GROUP BY channel
UNION SELECT channel,MAX(end_time) FROM xcp GROUP BY channel
UNION SELECT DISTINCT channel,to_date('20200317','yyyymmdd') FROM xcp
UNION SELECT DISTINCT channel,to_date('20200318','yyyymmdd') FROM xcp),

tmp2 AS(--每個時間節點連接到下個節點  形成時間段
SELECT a.channel,a.time,LEAD(a.time,1) OVER(PARTITION BY a.channel ORDER BY a.time) nexttime
FROM tmp1 a),

tmp3 AS(--每個時間段取中值
SELECT b.channel,b.TIME,b.nexttime,(b.nexttime-b.time)/2+b.time midtime
FROM tmp2 b
WHERE b.nexttime IS NOT NULL
AND to_char(b.TIME,'yyyymmdd')=20200317),

tmp4 AS(--若中值處於原始記錄中  則該段時間為通道開通時間 否則通道不開通
SELECT c.*,
CASE WHEN EXISTS (SELECT 1 FROM xcp o WHERE c.midtime BETWEEN o.begin_time AND o.end_time) THEN 1 ELSE 0 END *
(c.nexttime-c.time)*24 duration
FROM tmp3 c)

SELECT nvl(d.channel,'合計時長') 通道,d.TIME 開始時間,d.nexttime 結束時間,
SUM(duration) "通道開通時間(小時)" FROM tmp4 d
GROUP BY rollup((d.channel,d.TIME,d.nexttime))
ORDER BY 2;

image思路是在第一步取時間節點的時候單獨加入17日0點24點的時間點即可

優化:

上述代碼全表掃描5次,效率垃圾,小強優化到一次掃描搞定了,代碼

--第8的特征:下一條記錄開始時間  大於  截止當前行的最大結束時間;那麼就把這部分時間記下來,最後減掉即可
select (max(end_time) - min(begin_time)) * 24 -
       sum(decode(sign(next_begin_time - max_end_time),
                  1,
                  (next_begin_time - max_end_time) * 24,
                  0)) 通道開通時間
  from (select a.channel,
               a.begin_time,
               a.end_time,
               max(a.end_time) over(partition by a.channel order by a.begin_time rows between unbounded preceding and current row) max_end_time, --截止當前行的最大結束時間
               lead(a.begin_time, 1) over(partition by a.channel order by a.begin_time) next_begin_time --下一條記錄的開始時間
          from xcp a) tmp;

我用plsql也優化了一個出來

/*思路:   
第一步:兩兩合併,兩條記錄之間的關係只有兩種:有交集 和 無交集
        1)對於有交集的:兩兩合併,取MIN(begin_time),MAX(end_time)作為新記錄,
        2)對於無交集的:同樣取MIN(begin_time),MAX(end_time)作為新記錄,不過把中間空白部分計入duration_del
第二步:然後將第一步合併的新紀錄和下一條記錄再兩兩合併,以此類推,直至合併完所有記錄
第三步:結果就是 最終合併記錄的  end_time-begin_time-duration_del*/
DECLARE
    duration_del NUMBER:=0;--存儲無交集的兩兩記錄之間的空白時間
    --用於存儲合併後的時間
    begin_time_merge DATE; end_time_merge DATE;
    --用於輸入要查詢的時間段
    day1 DATE:=to_date(20200314,'yyyymmdd');
    day2 DATE:=to_date(20200330,'yyyymmdd');
BEGIN
    FOR i IN (SELECT ROWNUM rnow,aa.* FROM
                          (SELECT a.channel,GREATEST(a.begin_time, day1) begin_time,LEAST(a.end_time,day2) end_time
                            FROM xcp a WHERE NOT (end_time < day1 OR  begin_time> day2) ORDER BY 2)aa
                    )LOOP  --掃描一次全表
        IF i.rnow=1 THEN   --第一條記錄用於初始化begin_time_merge  end_time_merge
            begin_time_merge :=i.begin_time; end_time_merge:=i.end_time;
        ELSE
             IF i.begin_time>end_time_merge THEN
                duration_del:= duration_del+ (i.begin_time-end_time_merge)*24;--空白部分計入duration_del
             END IF;
             end_time_merge := GREATEST(end_time_merge,i.end_time);
        END IF;       
    END LOOP; 
     DBMS_OUTPUT.PUT_LINE((end_time_merge-begin_time_merge)*24-duration_del||'個小時通道開放');
END;
/

就這樣 以備後查


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 代碼分享鏈接 https://pan.baidu.com/s/1UM0grvpttHW9idisiqa6rA 提取碼:hx7c 圖示 項目結構 1.SelectAllUser.jsp 1 <%@ page language="java" contentType="text/html; charset ...
  • 問題:scott用戶導入dmp文件,提示沒有DBA許可權。 解決方法: 1.授予dba角色許可權: grant dba to scott; 2.導入數據: imp scott/1234@myDB file=G:\ac43_620.dmp full=y 3.取消dba角色許可權: revoke dba fr ...
  • 在windows 10 64位操作系統中安裝Oracle win64 11g R2資料庫軟體,啟動SQL Developer卻提示缺少快捷方式,具體如下: 網上查閱參考了一些網友的解決方案,記錄如下: 1.在Oracle官方網站下載windows 64位的sqldeveloper安裝包zip文件。 ...
  • 一、下載下載oracle11g安裝文件,一般包括win64_11gR2_database_1of2和win64_11gR2_database_2of2 2個文件,下載完成後解壓到一個文件中: 下載文件: 解壓之後的文件夾: 二、準備1.配置 NET Framework 3.5 當然也可以離線安裝。 ...
  • 本次項目開發的各模塊靈感來自於市面上已經成熟的各大電商項目(網上商城)、例如淘寶京東。項目開發時間為2020/2/27—2020/3/28。開發工具前期使用Eclipse創建的Dynamic Web項目(動態web項目),到中期使用IDEA開發工具將項目轉為Maven項目繼續開發(期間自學了Mave ...
  • 筆者最近遇到一則典型的因為sql中存在派生表造成的性能案例,通過改寫SQL改善了的性能,但當時並沒有弄清楚這其中的原因,派生表究竟是什麼原因會導致性能上的副作用。說來也巧,很快就無意中就看到下文中的提到的相關的派生表的介紹以及其特性之後,才發現個中緣由,本文基於此,用一個非常簡單的demo來演示該問 ...
  • Redis底層函數詳解 1. serverCron 函數 它負責管理伺服器的資源,並維持伺服器的正常運行。在執行 serverCron 函數的過程中會調用相關的子函數,如 trackOperationsPerSecond、SigtermHandler、clientsCron、databasesCro ...
  • 1 MongoDB 簡介 1.1 使用場景 (1)數據量大 (2)價值較低 對於這樣的數據,更適合於使用MangoDB來存儲數據 1.2 什麼是MangoDB MangoDB是一個跨平臺的面向 文檔(相當於Mysql中的一行記錄) 的資料庫,是NoSql資料庫中最熱門的一種。它介於關係和非關係資料庫 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...