2011-11-24 123 views
2

我有2个表(pcgroup和客户端pc)。假设今天的日期是24/11,我需要找到哪台电脑在线以及哪台电脑在过去2天内只使用mySQL不在线。mySQL连接表重复问题

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天。所以,如果你们能帮助我,我真的很感激。

+1

你到底想要完成什么?你想在SQL中做一个表达逻辑?当(在你的例子中)'pc1'和'pc3'属于不同的组时,会发生什么?为什么'pc1'与pc3'在同一行而不是(例如)'pc4'? –

回答

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 
    ) 
0

通过使用MySQL中的组作为跟随

GROUP BY offlinePC; 

你可以得到下面的结果。

*pcGroupID groupName  onlinePC  offlinePC* 
    1   defaultGroup  pc1    pc3 
    1   defaultGroup  pc2    pc4 
    1   defaultGroup  pc2    pc5 
+0

其实结果会是pc1 3x for onlinePC,pc2没有出现.. – sicKo

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比在线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) 
+0

我试图重新在mysql中查询,但是我得到了稍微不同的结果,其中我的默认组显示6行为pc3,pc4和pc5在pc1和pc2之后的offlinepc列中以新行显示。 pc1和pc2旁边的offlinepc字段为空。无论如何,这个答案得到了我的投票:) – sicKo

0

不要在SQL中执行表示逻辑。如果您需要以某种方式格式化数据,请使用客户端语言进行。 SQL用于检索和操作数据,而不是格式化!

顺便说一句,因为你实际上并不想过滤行以任何方式,只是想分类他们,你可以简单地SELECT * FROM clientpc,然后再决定如何根据该差异的每一行呈现给用户在当前日期和lastOnlineTime之间。

(您也可以轻松地加入clientpcpcgroup,但即使这样migh是不必要的,因为你会无论如何获取最,如果不从pcgroup所有行,所以客户端“加入”和/或“组“在你的用户界面的情况下可能会更合适。)