Проблема дублирования таблицы соединений MySQL - PullRequest
2 голосов
/ 24 ноября 2011

У меня есть 2 таблицы (pcgroup и client pc). Допустим, сегодня дата 24/11, мне нужно выяснить, какой ПК подключен к сети, а какой нет в сети за последние 2 дня, используя только 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 дней. Поэтому я очень ценю, если вы, ребята, можете мне помочь.

Ответы [ 4 ]

2 голосов
/ 25 ноября 2011

Я запустил экземпляр 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)
0 голосов
/ 30 ноября 2011

Не делайте логику представления в SQL.Если вам нужно отформатировать данные определенным образом, сделайте это на языке клиента.SQL предназначен для извлечения и манипулирования данными, а не для форматирования!

Кстати, так как на самом деле вы не хотите фильтровать строки, а просто хотите классифицировать их, вы можете просто SELECT * FROM clientpc, а затем решить, как представить каждую строку пользователю на основе разницы между текущей датой и lastOnlineTime.

(Вы также можете легко присоединиться clientpcс pcgroup, но даже это может быть ненужным, так как вы будете собирать большинство, если не все строки из pcgroup, так что клиентское "соединение" и / или "группа" в контексте вашего пользовательского интерфейса можетбыть более подходящим.)

0 голосов
/ 30 ноября 2011

Почему бы не использовать 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 голосов
/ 24 ноября 2011

Использовать group by в mysql следующим образом

GROUP BY offlinePC;

Вы можете получить следующий результат.

*pcGroupID    groupName        onlinePC       offlinePC*
  1           defaultGroup      pc1             pc3
  1           defaultGroup      pc2             pc4
  1           defaultGroup      pc2             pc5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...