2011-11-24 52 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所有行,所以客戶端「加入」和/或「組「在你的用戶界面的情況下可能會更合適。)