我激发了一个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'? –