2016-06-21 152 views
3

我想結合2個不同列的條件來查詢。這是我的原始查詢。你可以在sqlfiddle.com中測試它。兩列結合條件mysql

-- creating database first for test data 
create table attendance(Id int, DateTime datetime, Door char(20)); 
INSERT INTO attendance VALUES 
( 1, '2016-01-01 08:00:00', 'In'), 
( 2, '2016-01-01 09:00:00', 'Out'), 
( 3, '2016-01-01 09:15:00', 'In'), 
( 4, '2016-01-01 09:30:00', 'In'), 
( 5, '2016-01-01 10:00:00', 'Out'), 
( 6, '2016-01-01 15:00:00', 'In'); 

SELECT * FROM attendance; 
SELECT 
@id:[email protected]+1 Id, 
MAX(IF(Door = 'In', DateTime, NULL)) `Check In`, 
MAX(IF(Door = 'Out', DateTime, NULL)) `Check Out` 
FROM 
(SELECT 
*, 
CASE 
    WHEN 
     (Door != 'Out' AND @last_door = 'Out') 
    THEN @group_num:[email protected]_num+1 
    ELSE @group_num END door_group, 
    @last_door:=Door 
FROM attendance 
JOIN (SELECT @group_num:=1,@last_door := NULL) a 
) t JOIN (SELECT @id:=0) b 
GROUP BY t.door_group 
HAVING SUM(Door = 'In') > 0 AND SUM(Door = 'Out') > 0; 

//output 
+------+---------------------+---------------------+ 
| Id | Check In   | Check Out   | 
+------+---------------------+---------------------+ 
| 1 | 2016-01-01 08:00:00 | 2016-01-01 09:00:00 | 
| 2 | 2016-01-01 09:30:00 | 2016-01-01 10:00:00 | 
+------+---------------------+---------------------+ 

從上面的查詢中,我想再添加一列。

-- creating database first for test data 
create table attendance(Id int, DateTime datetime, Door char(20), Active_door char(20)); 
INSERT INTO attendance VALUES 
( 1, '2016-01-01 08:00:00', 'In', ''), 
( 2, '2016-01-01 09:00:00', 'Out', ''), 
( 3, '2016-01-01 09:15:00', 'In', ''), 
( 4, '2016-01-01 09:30:00', 'In', ''), 
( 5, '2016-01-01 09:35:00', '', 'On'), 
( 6, '2016-01-01 10:00:00', 'Out', ''), 
( 7, '2016-01-01 16:00:00', '', 'Off'); 

這是我對我的查詢所做的更改,但它不起作用。

SELECT * FROM attendance; 
SELECT 
@id:[email protected]+1 Id, 
MAX(IF(Door = 'In' OR Active_door = "On", DateTime, NULL)) `Check In`, 
MAX(IF(Door = 'Out' OR Active_door = "Off", DateTime, NULL)) `Check Out` 
FROM 
(SELECT 
*, 
CASE 
    WHEN 
     ((Door != 'Out' OR Active_door != "Off") AND (@last_door = 'Out' OR @last_door = 'Off')) 
    THEN @group_num:[email protected]_num+1 
    ELSE @group_num END door_group, 
    @last_door:=Door 
FROM attendance 
JOIN (SELECT @group_num:=1,@last_door := NULL) a 
) t JOIN (SELECT @id:=0) b 
GROUP BY t.door_group 
HAVING SUM(Door = 'In') > 0 OR SUM(Active_door = 'On') > 0 AND SUM(Door = 'Out') > 0 OR SUM(Active_door = 'Off') > 0; 

//output 
+------+---------------------+---------------------+ 
| Id | Check In   | Check Out   | 
+------+---------------------+---------------------+ 
| 1 | 2016-01-01 08:00:00 | 2016-01-01 09:00:00 | 
| 2 | 2016-01-01 09:35:00 | 2016-01-01 10:00:00 | 
| 3 | NULL    | 2016-01-01 16:00:00 | 
+------+---------------------+---------------------+ 

//my desire output 
+------+---------------------+---------------------+ 
| Id | Check In   | Check Out   | 
+------+---------------------+---------------------+ 
| 1 | 2016-01-01 08:00:00 | 2016-01-01 09:00:00 | 
| 2 | 2016-01-01 09:35:00 | 2016-01-01 16:00:00 | 
+------+---------------------+---------------------+ 

請幫助我們如何獲得所需的輸出。我想從兩欄中獲得最後一筆,最後一筆。先謝謝你。

+3

我upvoting將此作爲一個例子每mysql的問題應該如何被寫入保存幫助手動執行創建和數據加載。不這樣做(無論是在問題中還是在sqlfiddle中)都會導致很多人通過你的問題。謝謝。 – Drew

+0

從第6行到第7行,添加第3個'door-group'(導致您的第3行),因爲last_state值是out(從第6行開始),實際的門值是empty '(在第7行),所以'Door!='Out OR ...'是真的。這取決於你的邏輯(何時創建一個新組)如何改變這種狀況 - 我假設你需要檢查'!='''或類似的東西。你可能想在'OR @last_door ='Off''檢查你的代碼,因爲據我所知,這不會是真的(你可能打算使用另一個變量,比如'@ last_active_door'或者設置'@last_door: ='也取決於'active_door' – Solarflare

+0

我忘了添加:爲你的內部查詢添加一個命令,例如'order by id',或'order by DateTime'使它可靠。對你而言,你可能是幸運的您的數據已經由DateTime訂購(儘管這可能對您沒有任何問題) – Solarflare

回答

1

這努力保持解決方案易於維護,沒有完成最終查詢所有在一個鏡頭,這將幾乎加倍它的大小(在我心中)。這是因爲結果需要匹配,並在匹配的In和Out事件的一行中表示。最後,我使用了幾張工作表。它在存儲過程中實現。

存儲過程使用幾個帶有cross join的變量。將交叉連接想象爲初始化變量的機制。變量保持安全,所以我相信,這個document的精神經常在變量查詢中引用。引用的重要部分是安全處理一行中的變量,強制它們在使用它們的其他列之前被設置。這是通過greatest()least()函數實現的,這些函數的優先級高於不使用這些函數的變量。請注意,coalesce()經常用於相同的目的。如果他們的使用看起來很奇怪,比如把已知的數字中的最大數字大於0或0,那麼這是故意的。慎重強制設置變量的優先順序。

查詢中命名爲dummy2等的列是未使用輸出的列,但它們用於在greatest()或其他內部設置變量。這是上面提到的。 7777之類的輸出是第3個插槽中的佔位符,因爲所用的if()需要一些值。所以忽略這一切。

我已經包含了代碼的幾個屏幕截圖,因爲它逐層進行以幫助您可視化輸出。以及這些迭代的發展如何慢慢地進入下一階段,以擴展先前的發展。

我確定我的同事可以在一個查詢中對此進行改進。我本可以用這種方式完成它。但我相信這會導致一個混亂的混亂,如果感動就會破裂。

模式:

create table attendance2(Id int, DateTime datetime, Door char(20), Active_door char(20)); 
INSERT INTO attendance2 VALUES 
( 1, '2016-01-01 08:00:00', 'In', ''), 
( 2, '2016-01-01 09:00:00', 'Out', ''), 
( 3, '2016-01-01 09:15:00', 'In', ''), 
( 4, '2016-01-01 09:30:00', 'In', ''), 
( 5, '2016-01-01 09:35:00', '', 'On'), 
( 6, '2016-01-01 10:00:00', 'Out', ''), 
( 7, '2016-01-01 16:00:00', '', 'Off'); 

drop table if exists oneLinersDetail; 
create table oneLinersDetail 
( -- architect this depending on multi-user concurrency 
    id int not null, 
    dt datetime not null, 
    door int not null, 
    grpIn int not null, 
    grpInSeq int not null, 
    grpOut int not null, 
    grpOutSeq int not null 
); 

drop table if exists oneLinersSummary; 
create table oneLinersSummary 
( -- architect this depending on multi-user concurrency 
    id int not null, 
    grpInSeq int null, 
    grpOutSeq int null, 
    checkIn datetime null, -- we are hoping in the end it is not null 
    checkOut datetime null -- ditto 
); 

存儲過程:

DROP PROCEDURE IF EXISTS fetchOneLiners; 
DELIMITER $$ 
CREATE PROCEDURE fetchOneLiners() 
BEGIN 
    truncate table oneLinersDetail; -- architect this depending on multi-user concurrency 

    insert oneLinersDetail(id,dt,door,grpIn,grpInSeq,grpOut,grpOutSeq) 
    select id,dt,door,grpIn,grpInSeq,grpOut,grpOutSeq 
    from 
    ( select id,dt,door, 
     if(@lastEvt!=door and door=1, 
      greatest(@grpIn:[email protected]+1,0), 
      7777) as dummy2, -- this output column we don't care about (we care about the variable being set) 
     if(@lastEvt!=door and door=2, 
      greatest(@grpOut:[email protected]+1,0), 
      7777) as dummy3, -- this output column we don't care about (we care about the variable being set) 
     if (@lastEvt!=door,greatest(@flip:=1,0),least(@flip:=0,1)) as flip, 
     if (door=1 and @flip=1,least(@grpOutSeq:=0,1),7777) as dummy4, 
     if (door=1 and @flip=1,greatest(@grpInSeq:=1,0),7777) as dummy5, 
     if (door=1 and @flip!=1,greatest(@grpInSeq:[email protected]+1,0),7777) as dummy6, 
     if (door=2 and @flip=1,least(@grpInSeq:=0,1),7777) as dummy7, 
     if (door=2 and @flip=1,greatest(@grpOutSeq:=1,0),7777) as dummy8, 
     if (door=2 and @flip!=1,greatest(@grpOutSeq:[email protected]+1,0),7777) as dummy9, 
     @grpIn as grpIn, 
     @grpInSeq as grpInSeq, 
     @grpOut as grpOut, 
     @grpOutSeq as grpOutSeq, 
     @lastEvt:=door as lastEvt 
     from 
     ( select id,`datetime` as dt, 
      CASE 
       WHEN Door='in' or Active_door='on' THEN 1 
       ELSE 2 
      END as door 
      from attendance2 
      order by id 
     ) xD1 -- derived table #1 
     cross join (select @grpIn:=0,@grpInSeq:=0,@grpOut:=0,@grpOutSeq:=0,@lastEvt:=-1,@flip:=0) xParams 
     order by id 
    ) xD2 -- derived table #2 
    order by id; 
    -- select * from oneLinersDetail; 

    truncate table oneLinersSummary; -- architect this depending on multi-user concurrency 

    insert oneLinersSummary (id,grpInSeq,grpOutSeq,checkIn,checkOut) 
    select distinct grpIn,null,null,null,null 
    from oneLinersDetail 
    order by grpIn; 

    -- select * from oneLinersSummary; 

    update oneLinersSummary ols 
    join 
    ( select grpIn,max(grpInSeq) m 
     from oneLinersDetail 
     where door=1 
     group by grpIn 
    ) d1 
    on d1.grpIn=ols.id 
    set ols.grpInSeq=d1.m; 

    -- select * from oneLinersSummary; 

    update oneLinersSummary ols 
    join 
    ( select grpOut,max(grpOutSeq) m 
     from oneLinersDetail 
     where door=2 
     group by grpOut 
    ) d1 
    on d1.grpOut=ols.id 
    set ols.grpOutSeq=d1.m; 

    -- select * from oneLinersSummary; 

    update oneLinersSummary ols 
    join oneLinersDetail old 
    on old.door=1 and old.grpIn=ols.id and old.grpInSeq=ols.grpInSeq 
    set ols.checkIn=old.dt; 

    -- select * from oneLinersSummary; 

    update oneLinersSummary ols 
    join oneLinersDetail old 
    on old.door=2 and old.grpOut=ols.id and old.grpOutSeq=ols.grpOutSeq 
    set ols.checkOut=old.dt; 

    -- select * from oneLinersSummary; 

    -- dump out the results 
    select id,checkIn,checkOut 
    from oneLinersSummary 
    order by id; 
    -- rows are left in those two tables (oneLinersDetail,oneLinersSummary) 
END$$ 
DELIMITER ; 

測試:

call fetchOneLiners(); 
+----+---------------------+---------------------+ 
| id | checkIn    | checkOut   | 
+----+---------------------+---------------------+ 
| 1 | 2016-01-01 08:00:00 | 2016-01-01 09:00:00 | 
| 2 | 2016-01-01 09:35:00 | 2016-01-01 16:00:00 | 
+----+---------------------+---------------------+ 

這是答案的結尾。以下內容是開發人員對導致完成存儲過程的步驟的可視化。

開發的版本,直到最後。希望這有助於可視化,而不是僅僅丟棄中等大小的混淆代碼塊。

步驟A

enter image description here

步驟B

enter image description here

步驟B輸出

enter image description here

步驟C

enter image description here

步驟C輸出

enter image description here

+0

我嘗試「保持足夠的獨立」,但似乎無法獲得預期的結果。爲什麼我不能得到值「2016-01-01 16:00:00」的列「Active_door」的值?再次檢查我想要的輸出 – EDDY

+0

可能最好解釋它對我來說,我們可以將其總結爲一個編輯,我在這個叫做[Campaigns]的房間裏(http://chat.stackoverflow.com/rooms/95290 /廣告系列) – Drew

+0

哇..這是很多來自你@Drew的作品。我仍然深入你的代碼,讓自己瞭解整個過程。非常感謝您爲此案所做的全部努力!如果我不明白在查詢中使用的某個術語,會再次通知您。 :) – EDDY