iT邦幫忙

2

SQL語法如何整理?

  • 分享至 

  • xImage

各為SQL大神好!小弟目前有個SQL情境卡住,想求救,目前公司有兩張TABLE
一個示意如下:
https://ithelp.ithome.com.tw/upload/images/20220325/20147733VUxkxlSlIh.jpg
另一張表示意如下:
https://ithelp.ithome.com.tw/upload/images/20220325/20147733swgRZNfIdq.jpg
我這邊需要整理成以下的樣子:
https://ithelp.ithome.com.tw/upload/images/20220325/20147733Rj4IDiK5fk.jpg

主要就是兩張表都有的時間狀態為RUN
第一張有但第二張沒有的為PRERUN這樣,不曉得是否能夠單純用SQL解出來?
(這些表建在SQL SERVER)

稍微看了一下,結果表的有 RUN 都是表二的時間
而PRE RUN 是RUN以外的時間,先用表一跟表二找出 PRE RUN 的時間
再去組合表二跟 PRE RUN 這兩張表,然後按時間排序,大致上的邏輯就這樣
石頭 iT邦高手 1 級 ‧ 2022-03-25 17:33:54 檢舉
看起來這是一個 連續範圍問題 + CTE 遞迴 搭配 OUTER JOIN 問題 我下面有相關解答 提供給你, 因為你提供的資訊有點少 我只能用現有的資料寫這個版本
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
11
rogeryao
iT邦超人 7 級 ‧ 2022-03-25 20:17:20
最佳解答
CREATE TABLE XX (
MNo VARCHAR(20),
MType VARCHAR(20),
MStart time,
MEnd time);

INSERT INTO XX VALUES 
('CT-001','Run','01:00:00','02:00:00'),
('CT-002','Run','01:15:00','01:52:00');
CREATE TABLE YY (
MNo VARCHAR(20),
MType VARCHAR(20),
MStart time,
MEnd time);

INSERT INTO YY VALUES 
('CT-001','VA','01:10:00','01:20:00'),
('CT-001','CO','01:30:00','01:40:00'),
('CT-001','NO','01:50:00','01:55:00'),
--
('CT-002','2VB','01:03:00','01:05:00'),
('CT-002','2VA','01:10:00','01:20:00'),
('CT-002','2CO','01:30:00','01:40:00'),
('CT-002','2NO','01:50:00','01:55:00'),
('CT-002','2NH','02:00:00','02:10:00');
SELECT FF.MNo,
CASE WHEN FF.TStart >= QQ.MStart AND FF.TEnd <= QQ.MEnd 
AND FF.MARK1='S' AND FF.MARK2A='E' THEN 'Run' 
ELSE 'PreRun' END AS TType,
FF.TStart,FF.TEnd
FROM (
SELECT *,
LEAD(PP.TStart,1,NULL) OVER (PARTITION BY PP.MNo ORDER BY PP.TStart) AS TEnd,
LEAD(PP.MARK2,1,NULL) OVER (PARTITION BY PP.MNo ORDER BY PP.TStart) AS MARK2A
FROM (
SELECT MNo,MStart AS TStart,'S' AS MARK1,'' AS MARK2
FROM XX
UNION ALL
SELECT MNo,MEnd AS TStart,'' AS MARK1,'E' AS MARK2
FROM XX
--
UNION ALL
SELECT MNo,MStart AS TStart,'S' AS MARK1,'' AS MARK2
FROM YY
UNION ALL
SELECT MNo,MEnd AS TStart,'' AS MARK1,'E' AS MARK2
FROM YY
) AS PP) AS FF
LEFT JOIN XX AS QQ ON QQ.MNo = FF.MNo
WHERE FF.TEnd IS NOT NULL
ORDER BY FF.MNo,FF.TStart

Demo

看更多先前的回應...收起先前的回應...

/images/emoticon/emoticon35.gif

rogeryao iT邦超人 7 級 ‧ 2022-03-25 22:33:18 檢舉

玩玩而已......

尼克 iT邦大師 1 級 ‧ 2022-03-26 14:12:27 檢舉

cool

真的好強!非常感謝您的解惑rogeryao

0
海綿寶寶
iT邦大神 1 級 ‧ 2022-03-25 16:44:23

我覺得不能夠

0

這樣子有點難啦!!
沒基本對應欄位。要怎麼搞。

電腦是很白吃的。你要告訴他用啥做基本對應值。

0
尼克
iT邦大師 1 級 ‧ 2022-03-25 16:57:02

如樓上兩位答覆,沒有對應的Key值,要如何查詢出來?

6
石頭
iT邦高手 1 級 ‧ 2022-03-25 17:30:02

如果我理解正確,這是一個 連續範圍問題 + CTE 遞迴 搭配 OUTER JOIN 問題

;WITH CTE AS (
   SELECT Name,StartTime,EndTime
   FROM T1
   UNION ALL
   SELECT Name,DATEADD(MINUTE, 1, StartTime) ,EndTime
   FROM CTE 
   WHERE DATEADD(MINUTE, 1, StartTime) <= EndTime
) 
SELECT Name,
       MAX(CASE WHEN t2StartTime IS NULL THEN 'Pre-Run' ELSE 'RUN' END),
       MIN(CASE WHEN t2StartTime IS NULL AND DATEADD(MINUTE, - 1, StartTime) > minTime 
          THEN DATEADD(MINUTE, - 1, StartTime) ELSE StartTime END) starttime,
       MAX(CASE WHEN t2StartTime IS NULL AND DATEADD(MINUTE, 1, StartTime) < maxTime 
          THEN DATEADD(MINUTE, 1, StartTime) ELSE StartTime END) endtime
FROM (
 SELECT c.*,
        ROW_NUMBER() OVER(ORDER BY c.StartTime) -
           ROW_NUMBER() OVER(ORDER BY t2.StartTime) grp,
        t2.StartTime t2StartTime,
        MIN(c.starttime) OVER(partition by c.NAME) minTime,
        Max(c.starttime) OVER(partition by c.NAME) maxTime
  FROM CTE c 
  LEFT JOIN T2 t2
  ON  c.StartTime BETWEEN t2.StartTime AND t2.EndTime
  AND c.Name = t2.Name
) tt
GROUP BY grp,Name
ORDER BY 3

db<>fiddle here

看更多先前的回應...收起先前的回應...

看來又有 SQL 高手蒞臨本站了
/images/emoticon/emoticon34.gif/images/emoticon/emoticon34.gif/images/emoticon/emoticon34.gif

你太強了。我看完你的寫法。
還是看不太懂需求是啥。

主要是他的第二張表的TYPE是幹啥的?

大神好!剛剛針對大神的語法已經先建立表了,在執行語法時跳出以下錯誤,小弟我資質駑鈍,望請大神解惑,感謝!https://ithelp.ithome.com.tw/upload/images/20220325/20147733JtLhDTTi6x.jpg

rogeryao iT邦超人 7 級 ‧ 2022-03-25 22:52:48 檢舉

純真的人
好強,居然聯想到"機台稼動率"....好樣的

好樣的。居然可以聯想到那邊去。
他資訊給的太少了。

石頭 iT邦高手 1 級 ‧ 2022-03-26 19:44:56 檢舉

lovemin116 這個 Error 你可以把 查詢欄位 [Name] 使用中括號刮起來

石頭 iT邦高手 1 級 ‧ 2022-03-26 19:49:08 檢舉

純真的人 感謝分享 原來這個叫 機台稼動率

6
一級屠豬士
iT邦大師 1 級 ‧ 2022-03-25 19:04:50

使用 PostgreSQL 14, Range 以及 Multirange

create table it220325a (
  machine text
, mchstat text
, statrange tsrange
);

insert into it220325a values
('CT-001', 'RUN', tsrange('2022-03-25 01:00:00', '2022-03-25 02:00:00)', '[)'));

create table it220325b (
  machine text
, mchstat text
, statrange tsrange
, exclude using gist (statrange WITH &&)
);

insert into it220325b values
('CT-001', 'VACUUM', tsrange('2022-03-25 01:10:00', '2022-03-25 01:20:00', '[)')), 
('CT-001', 'COATING', tsrange('2022-03-25 01:30:00', '2022-03-25 01:40:00', '[)')),
('CT-001', 'NONVACUUM', tsrange('2022-03-25 01:50:00', '2022-03-25 01:55:00', '[)'));

--
with t1(machine, rangeagg) as (
select machine
     , range_agg(statrange)
  from it220325b
 group by machine
)
select a.machine
     , 'PRE-RUN' as "type"
     , unnest(tsmultirange(a.statrange) - t1.rangeagg) as "statrange"
  from it220325a a
  join t1
    on a.machine = t1.machine
union all
select a.machine
     , 'RUN'
     , b.statrange
  from it220325a a
  join it220325b b
    on a.machine = b.machine
   and a.statrange @> b.statrange
 order by 1,3;
 
machine  |  type   |                   statrange
---------+---------+-----------------------------------------------
 CT-001  | PRE-RUN | ["2022-03-25 01:00:00","2022-03-25 01:10:00")
 CT-001  | RUN     | ["2022-03-25 01:10:00","2022-03-25 01:20:00")
 CT-001  | PRE-RUN | ["2022-03-25 01:20:00","2022-03-25 01:30:00")
 CT-001  | RUN     | ["2022-03-25 01:30:00","2022-03-25 01:40:00")
 CT-001  | PRE-RUN | ["2022-03-25 01:40:00","2022-03-25 01:50:00")
 CT-001  | RUN     | ["2022-03-25 01:50:00","2022-03-25 01:55:00")
 CT-001  | PRE-RUN | ["2022-03-25 01:55:00","2022-03-25 02:00:00")
(7 rows)

https://ithelp.ithome.com.tw/upload/images/20220325/20050647OgKMtXznES.png

Demo

艾米 iT邦新手 4 級 ‧ 2022-03-25 19:17:17 檢舉

太神啦

/images/emoticon/emoticon07.gif

尼克 iT邦大師 1 級 ‧ 2022-03-26 14:11:42 檢舉

真是厲害!

3
Kevin
iT邦新手 1 級 ‧ 2022-03-25 20:19:59

參考樓上大大,提供一個簡單版本,將RUN和PRE-RUN拆開再合併

WITH RankTmp
AS     (SELECT T2.NAME,
        T2.StartTime,
        T2.EndTime,
        ROW_NUMBER() OVER(ORDER BY StartTime) ranks
        FROM   T2
        GROUP  BY T2.NAME, T2.StartTime, T2.EndTime)


        
SELECT A.*
FROM   (SELECT T2.NAME,
               'RUN'        AS 'Type',
               T2.StartTime AS 'StartTime',
               T2.EndTime   AS 'EndTime'
        FROM   T2
        
        UNION
        
        SELECT T2.NAME,
               'PRE-RUN'         AS 'Type',
               MIN(T1.StartTime) AS 'StartTime',
               Min(T2.StartTime) AS 'EndTime'
        FROM   T1 LEFT JOIN T2 
        ON T1.NAME = T2.NAME
        GROUP  BY T2.NAME
        
        UNION
        
        SELECT tmp1.NAME,
               'PRE-RUN' AS 'Type',
               tmp1.EndTime AS 'StartTime',
               (CASE WHEN tmp2.StartTime IS NULL THEN b.EndTime ELSE Dateadd(minute, Datediff(minute, tmp1.EndTime, tmp2.StartTime), tmp1.EndTime) END )  AS 'EndTime'
        FROM   ranktmp tmp1 LEFT JOIN ranktmp tmp2
        ON tmp1.ranks = tmp2.ranks - 1
        AND tmp1.NAME = tmp2.NAME
        LEFT JOIN T1 b ON tmp1.NAME = b.NAME) A
ORDER  BY A.NAME,
          A.Endtime 

znau_01
iT邦新手 5 級 ‧ 2022-03-28 14:56:11
【**此則訊息已被站方移除**】

我要發表回答

立即登入回答