Я запустил экземпляр MySQL и смоделировал rowid, используемый в решении Postgres.Сценарий создания:
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), генератор номеров строк псевдо, полные внешние объединения).Я не уверен, есть ли у mysql такая возможность, поэтому я не использовал эти функции.
Я думаю, что главное в том, что вы запрашиваете два разных списка, которые на самом деле не связаны: onlinePC и offlinePC.Вы просто хотите поставить два списка рядом.Для этого вы можете ввести псевдостолбец подсчета строк, чтобы создать связь между двумя списками.Шаг 1 генерирует список ПК, подключенных к сети, и подсчитывает, сколько их существует для каждой группы (генерирует идентификатор строки, который равен _. Затем он присоединяет его к списку автономных ПК на основе этого идентификатора строки. Если ПК в автономном режиме было больше, чемонлайн-ПК, автономные ПК не будут отображаться в этом списке, поэтому на шаге 4 мы снова все делаем, но на этот раз на основе автономных ПК, чтобы учесть случай, когда ПК в автономном режиме больше, чем в сети.ПК. UNION избавится от дубликатов.
Я также использовал 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)