Как определить столбцы с повторяющимися записями (используя SQL)?

Мы создаем базу данных, которая будет использоваться для проверки любых дубликатов текущих имен пользователей, используемых сотрудниками в различных системах компании. Раньше некоторые сотрудники использовали одно и то же имя пользователя для доступа к некоторым системам. Поскольку цель состоит в том, чтобы иметь уникальное имя пользователя для каждого сотрудника в каждой системе, нам необходимо определить, какие сотрудники все еще используют общий доступ. В базе данных есть одна таблица, содержащая имена сотрудников и их соответствующие имена пользователей.

Пример: Таблица1

Employee    System1 System2  System3
John Doe    dJohn   Pkls453  xfd801
Jane Doe    dJane   Pkls454  xfd801
James Lee   dJames  Pkls455  fd674
Mark Jones  dMark   Pkls453  xfd752

Нам нужно сгенерировать отчет, указывающий, что Джон Доу и Джейн Доу используют один и тот же доступ к System3, и что Джон Доу и Марк Джонс используют общий доступ к System2. Что-то типа:

Employee  System3  System2
John Doe  xfd801
Jane Doe  xfd801
John Doe           Pkls453
Mark Jones         Pkls453

Есть ли способ снять это?

Заранее спасибо...


person Arnel del Moro    schedule 14.08.2012    source источник
comment
Вы уверены, что имена уникальны? Пожалуйста, скажи мне, что у тебя есть лучший уникальный идентификатор...   -  person Clockwork-Muse    schedule 15.08.2012
comment
Как всегда, сохранение в секрете фактической СУБД не поможет. От этого напрямую зависит наилучшее решение.   -  person Erwin Brandstetter    schedule 15.08.2012
comment
На самом деле идентификационный номер сотрудника также является частью таблицы (извините, забыл записать его в исходном запросе). Он служит уникальным идентификатором для каждой записи...   -  person Arnel del Moro    schedule 15.08.2012
comment
Я спрашиваю еще раз: пожалуйста, назовите СУБД (систему баз данных), которую вы используете. Не существует универсального SQL, как вы, кажется, думаете. Ни одна из систем полностью не соответствует стандарту.   -  person Erwin Brandstetter    schedule 15.08.2012


Ответы (2)


Я уверен, что есть более чистое решение, но оно должно возвращать то, что вы ищете, в указанном вами формате.

SELECT Employee, System1, NULL AS System2, NULL AS System3
FROM your_table T1
WHERE EXISTS(SELECT * FROM your_table T2
         WHERE T1.System1 = T2.System1
         AND T1.Employee <> T2.Employee)
UNION
SELECT Employee, NULL AS System1, System2, NULL AS System3
FROM your_table T1
WHERE EXISTS(SELECT * FROM your_table T2
         WHERE T1.System2 = T2.System2
         AND T1.Employee <> T2.Employee)
UNION
SELECT Employee, NULL AS System1, NULL AS System2, System3
FROM your_table T1
WHERE EXISTS(SELECT * FROM your_table T2
         WHERE T1.System3 = T2.System3
         AND T1.Employee <> T2.Employee)
ORDER BY System1, System2, System3
person Jon Collins    schedule 14.08.2012
comment
+1 - Для систем без необходимых оконных функций это может быть единственный способ найти нужную информацию без создания (дополнительных, дополнительных) дубликатов в наборах результатов. Вы можете либо обернуть все это в ORDER BY, либо дать каждому оператору соответствующее предложение, чтобы вывод выглядел так, как указано в OP. - person Clockwork-Muse; 15.08.2012

Если ваша система поддерживает оконные функции, это будет работать:

SELECT employee, system1, system2, system3
FROM  (
   SELECT employee
         ,system1
         ,cast(NULL AS text) AS system2
         ,cast(NULL AS text) AS system3
         ,count(*) OVER (PARTITION BY system1) AS ct
   FROM tbl1

   UNION  ALL
   SELECT employee
         ,NULL -- cast and column name only needed in first SELECT in Postgres
         ,system2
         ,NULL
         ,count(*) OVER (PARTITION BY system2) AS ct
   FROM   tbl1

   UNION  ALL
   SELECT employee
         ,NULL
         ,NULL
         ,system3
         ,count(*) OVER (PARTITION BY system3) AS ct
   FROM   tbl1
   ) x
WHERE  ct > 1
ORDER  BY system1, system2, system3;

Или, возможно, быстрее:
Обратите внимание, что "John Doe", совместно использующий несколько систем, указан только один раз в следующих запросах (в отличие от первого) со всеми его общими системами. Для систем без общего доступа установлено значение NULL.

SELECT employee
      ,CASE WHEN ct1 > 1 THEN system1 ELSE NULL END AS system1
      ,CASE WHEN ct2 > 1 THEN system2 ELSE NULL END AS system2
      ,CASE WHEN ct3 > 1 THEN system3 ELSE NULL END AS system3
FROM   (
    SELECT employee, system1, system2, system3
          ,count(*) OVER (PARTITION BY system1) AS ct1
          ,count(*) OVER (PARTITION BY system2) AS ct2
          ,count(*) OVER (PARTITION BY system3) AS ct3
    FROM tbl1
    ) x
WHERE  ct1 > 1 OR ct2 > 1 OR ct3 > 1
ORDER  BY system1, system2, system3; -- depends on what you want

Или, если ваша анонимная система поддерживает общие табличные выражения:

WITH x AS (
    SELECT employee, system1, system2, system3
          ,count(*) OVER (PARTITION BY system1) AS ct1
          ,count(*) OVER (PARTITION BY system2) AS ct2
          ,count(*) OVER (PARTITION BY system3) AS ct3
    FROM tbl1
    )
SELECT employee
      ,CASE WHEN ct1 > 1 THEN system1 ELSE NULL END AS system1
      ,CASE WHEN ct2 > 1 THEN system2 ELSE NULL END AS system2
      ,CASE WHEN ct3 > 1 THEN system3 ELSE NULL END AS system3
FROM   x
WHERE  ct1 > 1 OR ct2 > 1 OR ct3 > 1
ORDER  BY system1, system2, system3; -- depends

Если у вас нет ни CTE, ни оконных функций:
(Должно работать со всеми основными СУБД, включая MySQL.)

SELECT t.employee, s1.system1, s2.system2, s3.system3
FROM   tbl1 t
LEFT   JOIN (SELECT system1 FROM tbl1 GROUP BY 1 HAVING count(*) > 1) s1
                                                 ON t.system1 = s1.system1
LEFT   JOIN (SELECT system2 FROM tbl1 GROUP BY 1 HAVING count(*) > 1) s2
                                                 ON t.system2 = s2.system2
LEFT   JOIN (SELECT system3 FROM tbl1 GROUP BY 1 HAVING count(*) > 1) s3
                                                 ON t.system3 = s3.system3
WHERE s1.system1 IS NOT NULL
   OR s2.system2 IS NOT NULL
   OR s3.system3 IS NOT NULL
ORDER BY s1.system1, s2.system2, s3.system3; -- depends

Протестировано с PostgreSQL 9.1.4.

person Erwin Brandstetter    schedule 14.08.2012
comment
Хм, я думаю, мне больше всего нравится ваше второе редактирование с точки зрения чистоты (скорость вряд ли будет проблемой, учитывая, что это должно быть в значительной степени единовременной платой). Однако вы можете добавить ORDER BY, чтобы получить отображение, которое хочет OP. - person Clockwork-Muse; 15.08.2012
comment
Вау, это был быстрый ответ! Спасибо, Джон и Эрвин, я попробую ваши решения, когда вернусь в офис завтра..ü - person Arnel del Moro; 15.08.2012
comment
@X-Zero: я добавил ORDER BY. Не совсем уверен, что ОП захочет для запросов 2 и 3. - person Erwin Brandstetter; 15.08.2012
comment
Хорошо, я должен признать, что когда я обдумывал JOINs, я не придумал это. - person Clockwork-Muse; 15.08.2012