Может ли кто-нибудь показать мне, почему мой SQL-запрос не работает (см. подробности)?

Я использовал следующий запрос для поиска дубликатов:

SELECT userID,
COUNT(userID) AS NumOccurrences
FROM userDepartments
GROUP BY userID
HAVING ( COUNT(userID) > 1 )

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

SELECT userDepartments.userID, users.firstname, users.lastname,
COUNT(userID) AS NumOccurrences
FROM userDepartments INNER JOIN users ON userDepartments.userID = users.userID
GROUP BY userID
HAVING ( COUNT(userID) > 1 )

Но это дало мне сообщение об ошибке, в котором говорилось, что users.firstname не является частью какой-то агрегатной функции или чего-то в этом роде...

Кто-нибудь знает, как я могу получить подсчет, показать только пользователей с более чем 1 отделом, а также получить имя и фамилию из другой таблицы, чтобы я мог получить список имен пользователей, которым назначено более одного отдела?

РЕДАКТИРОВАТЬ: ЭТО ЗАПРОС, КОТОРЫЙ ЗАКОНЧИЛСЯ РАБОТАТЬ ДЛЯ МЕНЯ...

SELECT     firstname, lastname
FROM         tbl_users
WHERE     (userID IN
                          (SELECT     userID
                            FROM          tbl_usersDepts
                            GROUP BY userID
                            HAVING      (COUNT(userID) > 1)))

person Ryan    schedule 06.02.2009    source источник
comment
Не забудьте указать человека, который дал вам лучший ответ на ваше решение; даже если вы придумали это в конце концов. Кто-то здесь, должно быть, дал вам ногу. Слава решению.   -  person SnapJag    schedule 07.02.2009


Ответы (8)


Я бы немного переформулировал запрос....

SELECT
    duplicates.NumOccurrences,
    duplicates.userID,
    users.firstname,
    users.lastname
FROM (
    SELECT
        userID,
        COUNT(userID) AS NumOccurrences
    FROM userDepartments
    GROUP BY userID
    HAVING COUNT(userID) > 1
) duplicates
INNER JOIN users ON duplicates.userID = users.userID
person yfeldblum    schedule 06.02.2009
comment
Опереди меня на пару секунд :) - person Dave Costa; 06.02.2009
comment
Я тоже, хотя я смог вернуться и немного различить свою. - person Joel Coehoorn; 07.02.2009
comment
Предполагая, что идентификатор пользователя имеет только одно имя/фамилию, может ли кто-нибудь объяснить преимущество выполнения этого в подзапросе и присоединения результата к пользователям по сравнению с присоединением сначала к пользователям, а затем группировкой по всем трем полям? - person jimmyorr; 07.02.2009
comment
Потому что вы четко отделяете исходные данные от красивого оформления исходных данных. - person yfeldblum; 07.02.2009
comment
Я не куплюсь на это. Если вам нужно добавить еще одно поле в выборку, добавьте его и в группу, ничего страшного в этом нет. После того, как вы сгруппировали по идентификатору пользователя, вы не платите больше за группировку по дополнительным полям. - person jimmyorr; 07.02.2009
comment
Также стоит отметить, что Oracle CBO, скорее всего, объединит ваше представление дубликатов и приведет к той же стоимости / плану, что и моя версия, но в других базах данных ваше красивое определение может повлиять на производительность. - person jimmyorr; 07.02.2009
comment
Эту версию легче понять. Вдвойне, когда вам нужно преобразовывать данные на нескольких уровнях и разными способами. Вот когда приятно иметь возможность взглянуть на цепочку, где каждое звено представляет собой подзапрос размером с укус. - person yfeldblum; 07.02.2009
comment
проще понять немного субъективно. Объективно, с другой стороны, это решение будет считывать всю таблицу userDepartments в память для создания дубликатов встроенного представления, даже если вы можете отфильтровать набор результатов, используя предикат для таблицы пользователей. Опять же, это без помощи CBO. - person jimmyorr; 07.02.2009
comment
Думайте об этом как о каналах Unix. Когда запросы становятся большими и сложными и теряют смысл, тогда самое время превратить ваши запросы в цепочки небольших подзапросов. - person yfeldblum; 07.02.2009
comment
Ваш подзапрос не обязательно имеет размер укуса. Без сложного слияния представлений CBO вы всегда считываете всю таблицу userDepts в встроенное представление. Что, если userDepts огромен, и вы можете сократить его с помощью предикатов в другой таблице (например, пользователи). Я использую только Oracle, объединяются ли другие базы данных? - person jimmyorr; 07.02.2009
comment
Да, если вам нужно сократить исходный подзапрос, сделайте это во что бы то ни стало... дело в том, что, поскольку данный пример касается добавления дополнительной информации и выравнивания результирующего набора, аспект выравнивания должен быть отделились. Мой ответ отделяет исходный запрос от красоты. - person yfeldblum; 07.02.2009

Механизм SQL не знает, что у вас есть только одно имя пользователя для каждого идентификатора пользователя, поэтому вам нужно группировать по имени и фамилии, а также по идентификатору пользователя.

SELECT userDepartments.userID, users.firstname, users.lastname,
COUNT(userID) AS NumOccurrences
FROM userDepartments INNER JOIN users ON userDepartments.userID = users.userID
GROUP BY userID, users.firstname, users.lastname
HAVING ( COUNT(userID) > 1 )

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

Вы также можете сделать это следующим образом:

SELECT users.userId, users.firstname, users.lastname, departments.NumOccurrences
FROM users INNER JOIN (
     SELECT userId, count(userId) as NumOccurrences 
     FROM userDepartments 
     GROUP BY userID 
     HAVING ( COUNT(userID) > 1 )
) departments ON departments.userID = users.userID
person Eclipse    schedule 06.02.2009

Сгруппируйте по всем трем параметрам: userDepartments.userID, users.firstname и users.lastname.

person jimmyorr    schedule 06.02.2009

Вам необходимо включить user.firstname и users.lastname в предложение GROUP BY, поскольку они не являются агрегированными значениями (обратите внимание, что MySQL действительно поддерживает синтаксис, который вы использовали в своем запросе, но он не является стандартным).

person BrynJ    schedule 06.02.2009

Если вы выполняете «группировку», то все в части «выбрать» должно быть:

  1. Упоминается в предложении «group by» или

  2. Результат агрегатной функции (например, count())

person Clinton Pierce    schedule 06.02.2009

Я бы сделал это так (в Oracle, на случай, если это не работает в вашей системе):

SELECT users.userID, users.firstname, users.lastname, NumOccurrences
  FROM users
       INNER JOIN (
         SELECT userID, COUNT(userID) AS NumOccurrences
           FROM userDepartments
           GROUP BY userID
           HAVING ( COUNT(userID) > 1 )
       ) d
       ON d.userID = users.userID
person Dave Costa    schedule 06.02.2009

Добавьте свой user.Firstname и User.lastname в свою группу по предложению

person Eppz    schedule 06.02.2009

Я вижу много хороших заметок о добавлении полей вашего имени в группу. Я думаю, что я бы сделал это так, хотя:

SELECT Users.*, dups.NumOccurances, ud.DepartmentName
FROM Users
INNER JOIN
  (
    SELECT userID, COUNT(userID) AS NumOccurrences
    FROM userDepartments
    GROUP BY userID
    HAVING ( COUNT(userID) > 1 )
  ) dups ON dups.userID = Users.UserID
INNER JOIN userDepartments ud ON ud.UserID=Users.UserID
ORDER BY Users.LastName, Users.FirstName, Users.UserID

Одна из причин такого подхода заключается в том, что с его помощью легче вернуться назад и получить любую другую информацию, которая вам может понадобиться.

person Joel Coehoorn    schedule 06.02.2009
comment
Двое других опередили меня менее чем на минуту. Удаление моего дубликата. - person Joel Coehoorn; 07.02.2009
comment
На самом деле, я думаю, что все-таки могу добавить ценности. У меня будет обновление через несколько минут. - person Joel Coehoorn; 07.02.2009