我有2个表(pcgroup和客户端pc)。假设今天的日期是24/11,我需要在过去2天内只使用mySQL查找哪台PC在线以及哪台PC不在线。
INSERT INTO pcgroup(id, groupName)
VALUES(1, 'defaultGroup');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime)
VALUES(1, 1, 'pc1', '2011-11-24');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime)
VALUES(2, 1, 'pc2', '2011-11-24');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime)
VALUES(3, 1, 'pc3', '2011-11-20');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime)
VALUES(4, 1, 'pc4', '2011-11-20');
INSERT INTO clientpc(id, pcGroupId, clientPcName, lastOnlineTime)
VALUES(5, 1, 'pc5', '2011-11-20');
这是我现在的查询
SELECT DISTINCT
pcgroup.id AS pcGroupID, pcgroup.groupName,
online.onlinePC, offline.offlinePC
FROM
(
SELECT
pcgroup.Id, pcGroup.groupName
FROM
pcgroup
WHERE
(pcgroup.Id = 1)
) pcgroup
LEFT JOIN
(
SELECT
clientpc.Id, clientpc.pcGroupId, clientpc.clientPcName AS onlinePC
FROM
clientpc
WHERE
DateDiff(CURDATE(),clientpc.lastOnlineTime) <= 2
AND
DateDiff(CURDATE(),clientpc.lastOnlineTime) IS NOT NULL
) online
ON
pcgroup.Id = online.pcGroupId
LEFT JOIN
(
SELECT
clientpc.Id, clientpc.pcGroupId, clientpc.clientPcName AS offlinePC
FROM
clientpc
WHERE
(DateDiff(CURDATE(),clientpc.lastOnlineTime) > 2
OR
DateDiff(CURDATE(),clientpc.lastOnlineTime) IS NULL)
) offline
ON pcgroup.Id = offline.pcGroupId
这是我得到的结果
*pcGroupID groupName onlinePC offlinePC*
1 defaultGroup pc1 pc3
1 defaultGroup pc1 pc4
1 defaultGroup pc1 pc5
1 defaultGroup pc2 pc3
1 defaultGroup pc2 pc4
1 defaultGroup pc2 pc5
然而,我需要的是这样的东西
*pcGroupID groupName onlinePC offlinePC*
1 defaultGroup pc1 pc3
1 defaultGroup pc2 pc4
1 defaultGroup pc5
所以我的问题是,这是可以实现的吗?如果是的话,如何。已经处理了这个查询2天了。如果你们能帮助我,我真的很感激。
答案 0 :(得分:2)
我启动了一个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在线电脑,离线电脑不会出现在这个列表中。这就是为什么我们在第4步再次完成整个事情,但这次是由离线电脑驱动,以解决离线电脑比在线更多的情况PC.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)
答案 1 :(得分:0)
在mysql中使用group by,如下所示
GROUP BY offlinePC;
您可以获得以下结果。
*pcGroupID groupName onlinePC offlinePC*
1 defaultGroup pc1 pc3
1 defaultGroup pc2 pc4
1 defaultGroup pc2 pc5
答案 2 :(得分:0)
为什么不使用2个简单的查询并在您的应用程序中进行演示?:
--- Online ---
SELECT
c.pcGroupID, p.groupName, c.clientPcName, 'Online' AS pcStatus
FROM
pcgroup AS g
JOIN
clientpc AS c
ON g.Id = c.pcGroupId
WHERE
g.Id = @pcGroupIdToBeChecked
AND
c.lastOnlineTime >= CURDATE() - INTERVAL 2 DAY
--- Offline ---
SELECT
c.pcGroupID, p.groupName, c.clientPcName, 'Offline' AS pcStatus
FROM
pcgroup AS g
JOIN
clientpc AS c
ON g.Id = c.pcGroupId
WHERE
g.Id = @pcGroupIdToBeChecked
AND
( c.lastOnlineTime < CURDATE() - INTERVAL 2 DAY
OR
c.lastOnlineTime IS NULL
)
答案 3 :(得分:0)
不要在SQL中执行表示逻辑。如果您需要以某种方式格式化数据,请使用您的客户端语言进行格式化。 SQL用于检索和操作数据,而不是格式化!
BTW,因为你实际上并不想以任何方式过滤行,只想分类它们,你可以简单地SELECT * FROM clientpc
,然后决定如何根据当前日期与lastOnlineTime
之间的差异向用户显示每一行。
(您也可以轻松地与clientpc
加入pcgroup
,但即便如此,因为您打算从pcgroup
获取大部分(如果不是全部)行,因此,在UI的上下文中,客户端“加入”和/或“组”可能更合适。)