我激發了一個MySQL實例並模擬了Postgres解決方案中使用的rowid。創建腳本:
CREATE TABLE pcgroup(id int, groupName varchar(64));
CREATE TABLE clientpc(id int, pcGroupId int, clientPcName varchar(64), lastOnlineTime date);
INSERT INTO pcgroup(id, groupName) VALUES(1, 'defaultGroup');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(1, 1, 'pc1', CURRENT_DATE);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(2, 1, 'pc2', CURRENT_DATE);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(3, 1, 'pc3', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(4, 1, 'pc4', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(5, 1, 'pc5', CURRENT_DATE-4);
INSERT INTO pcgroup(id, groupName) VALUES(2, 'group2');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(6, 2, 'pc6', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(7, 2, 'pc7', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(8, 2, 'pc8', CURRENT_DATE);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(9, 2, 'pc9', CURRENT_DATE);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(10, 2, 'pc10', CURRENT_DATE);
該腳本(如下相同的邏輯的Postgres溶液):
-- Apply sort to union
SELECT pcGroupID, groupName, onlinePC, offlinePC
FROM (
SELECT online.pcGroupID, online.groupName, online.clientPcName AS onlinePC, IFNULL(offline.clientPcName, '-') AS offlinePC
FROM (-- Apply a groupName-based row number to the list of "online" PCs
SELECT pcGroupID, groupName, clientPcName, lastOnlineTime
,if(@lastGroupID!=pcGroupID
,CONCAT_WS('_', pcGroupID, @curRow := 1)
,CONCAT_WS('_', pcGroupID, @curRow := @curRow + 1)) AS row_number
,@lastGroupID := pcGroupID
FROM (-- Filter to the list of online PCs
SELECT g.id AS pcGroupID, g.groupName, c.clientPcName, c.lastOnlineTime
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND c.lastOnlineTime >= CURRENT_DATE - 2
ORDER BY g.id, c.clientPcName) x
,(SELECT @curRow := 0) r) AS online
LEFT OUTER JOIN (
-- Apply a groupName-based row number to the list of "offline" PCs
SELECT pcGroupID, groupName, clientPcName, lastOnlineTime
,if(@lastGroupID!=pcGroupID
,CONCAT_WS('_', pcGroupID, @curRow := 1)
,CONCAT_WS('_', pcGroupID, @curRow := @curRow + 1)) AS row_number
,@lastGroupID := pcGroupID
FROM (-- Filter to the list of offline PCs
SELECT g.id AS pcGroupID, g.groupName, c.clientPcName, c.lastOnlineTime
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND c.lastOnlineTime < CURRENT_DATE - 2
ORDER BY g.id, c.clientPcName) x
,(SELECT @curRow := 0) r) AS offline
ON (online.row_number = offline.row_number)
UNION
SELECT offline.pcGroupID, offline.groupName, IFNULL(online.clientPcName, '~') AS onlinePC, offline.clientPcName AS offlinePC
FROM (-- Apply a groupName-based row number to the list of "online" PCs
SELECT pcGroupID, groupName, clientPcName, lastOnlineTime
,if(@lastGroupID!=pcGroupID
,CONCAT_WS('_', pcGroupID, @curRow := 1)
,CONCAT_WS('_', pcGroupID, @curRow := @curRow + 1)) AS row_number
,@lastGroupID := pcGroupID
FROM (-- Filter to the list of online PCs
SELECT g.id AS pcGroupID, g.groupName, c.clientPcName, c.lastOnlineTime
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND c.lastOnlineTime >= CURRENT_DATE - 2
ORDER BY g.id, c.clientPcName) x
,(SELECT @curRow := 0) r) AS online
RIGHT OUTER JOIN (
-- Apply a groupName-based row number to the list of "offline" PCs
SELECT pcGroupID, groupName, clientPcName, lastOnlineTime
,if(@lastGroupID!=pcGroupID
,CONCAT_WS('_', pcGroupID, @curRow := 1)
,CONCAT_WS('_', pcGroupID, @curRow := @curRow + 1)) AS row_number
,@lastGroupID := pcGroupID
FROM (-- Filter to the list of offline PCs
SELECT g.id AS pcGroupID, g.groupName, c.clientPcName, c.lastOnlineTime
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND c.lastOnlineTime < CURRENT_DATE - 2
ORDER BY g.id, c.clientPcName) x
,(SELECT @curRow := 0) r) AS offline
ON (online.row_number = offline.row_number)
) z ORDER BY pcGroupID, groupName, OnlinePC, offlinePC
而結果:
1 defaultGroup pc1 pc3
1 defaultGroup pc2 pc4
1 defaultGroup ~ pc5
2 group2 pc10 pc6
2 group2 pc8 pc7
2 group2 pc9 -
- PostgreSQL的 -
我在Postgres中試用過。該查詢看起來更可怕,然後確實如此。有許多功能可以縮短這一點:子查詢因子分解(即使用WITH),一個pseduo行號生成器,完整的外連接)。我不確定mysql是否有這個功能,所以我沒有使用這些功能。
我認爲重點是你要求兩個不相關的列表,它們並不真正相關:onlinePCs和offlinePCs。你只是想把兩個列表並排放置。要做到這一點,您可以引入一個行數僞列來創建兩個列表之間的關係。步驟1生成在線PC列表,並計算每個組的數量(生成一個行標識符_),然後根據此行標識符將其加入到脫機PC列表中。如果有更多的脫機PC比在線PC上,離線PC不會出現在這個列表中,這就是爲什麼我們在第4步中再次完成整個事情的原因,但這次是由離線PC驅動的,以說明離線PC比在線更多的情況個人電腦。聯盟將擺脫重複。
我也用CURRENT_DATE和硬編碼的2作爲離線和在線之間的天數,你將需要玩。
創建腳本:
CREATE TABLE pcgroup(id bigint, groupName varchar);
CREATE TABLE clientpc(id bigint, pcGroupId bigint, clientPcName varchar, lastOnlineTime date);
INSERT INTO pcgroup(id, groupName) VALUES(1, 'defaultGroup');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(1, 1, 'pc1', CURRENT_DATE);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(2, 1, 'pc2', CURRENT_DATE);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(3, 1, 'pc3', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(4, 1, 'pc4', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(5, 1, 'pc5', CURRENT_DATE-4);
INSERT INTO pcgroup(id, groupName) VALUES(2, 'group2');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(6, 2, 'pc6', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(7, 2, 'pc7', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(8, 2, 'pc8', CURRENT_DATE-4);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(9, 2, 'pc9', CURRENT_DATE);
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime) VALUES(10, 2, 'pc10', CURRENT_DATE);
查詢:
SELECT online.pcGroupID, online.groupName, online.clientPcName AS onlinePC, offline.clientPcName AS offlinePC
-- 1: Get the list of online PCs, and give them a group based pseudo rownumber
FROM (SELECT g.id AS pcGroupID, g.groupName, c.clientPcName
,g.id || '_' || row_number() OVER(PARTITION BY g.id ORDER BY g.id, c.clientPcName) AS rownum
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND lastOnlineTime > CURRENT_DATE - 2) AS online
-- 2: Get the list of offline PCs, and give them a group based pseudo rownumber
LEFT OUTER JOIN (SELECT g.id AS pcGroupID, g.groupName, c.clientPcName
,g.id || '_' || row_number() OVER(PARTITION BY g.id ORDER BY g.id, c.clientPcName) AS rownum
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND lastOnlineTime <= CURRENT_DATE - 2) AS offline
-- 3: Join the list together: this will only include rows for the number of "online" pcs that exist
ON (online.rownum = offline.rownum)
-- 4: Repeat 1-3, but this time base it on offline pcs and it will only include rows for the number of "offline" pcs that exist
-- The UNION will dump the duplicates
UNION
SELECT offline.pcGroupID, offline.groupName, online.clientPcName AS onlinePC, offline.clientPcName AS offlinePC
FROM (SELECT g.id AS pcGroupID, g.groupName, c.clientPcName
,g.id || '_' || row_number() OVER(PARTITION BY g.id ORDER BY g.id, c.clientPcName) AS rownum
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND lastOnlineTime > CURRENT_DATE - 2) AS online
RIGHT OUTER JOIN (SELECT g.id AS pcGroupID, g.groupName, c.clientPcName
,g.id || '_' || row_number() OVER(PARTITION BY g.id ORDER BY g.id, c.clientPcName) AS rownum
FROM pcgroup g
,clientpc c
WHERE c.pcGroupId = g.id
AND lastOnlineTime <= CURRENT_DATE - 2) AS offline
ON (online.rownum = offline.rownum)
結果:
pcgroupid | groupname | onlinepc | offlinepc
-----------+--------------+----------+-----------
1 | defaultGroup | pc1 | pc3
1 | defaultGroup | pc2 | pc4
1 | defaultGroup | | pc5
2 | group2 | pc10 | pc6
2 | group2 | pc8 | pc7
2 | group2 | pc9 |
(6 rows)
你到底想要完成什麼?你想在SQL中做一個表達邏輯?當(在你的例子中)'pc1'和'pc3'屬於不同的組時,會發生什麼?爲什麼'pc1'與pc3'在同一行而不是(例如)'pc4'? –