Выберите ровно одну строку для каждого сотрудника, используя неупорядоченное поле в качестве критерия - PullRequest
5 голосов
/ 28 июня 2010

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

EMPLID  PHONE_TYPE  PHONE
------  ----------  --------
100     HOME        111-1111
100     WORK        222-2222
101     HOME        333-3333
102     WORK        444-4444
103     OTHER       555-5555

Я хочу выбрать ровно одну строку для каждого сотрудника, используя поле PHONE_TYPE для установки предпочтений. Я хочу номер телефона HOME, если у сотрудника есть такой, как в случае сотрудников 100 и 101. Если номер HOME отсутствует, мне нужен номер РАБОТЫ (сотрудник 102), и в качестве последнего средства я возьму ДРУГОЕ число, как у сотрудника 103. На самом деле в моей таблице содержится около десятка значений для поля PHONE_TYPE, поэтому мне нужно иметь возможность расширить любое решение, включив в него не только три значения, которые я показал в примере. Какие-нибудь мысли? Спасибо.

Ответы [ 4 ]

3 голосов
/ 28 июня 2010

Вам необходимо добавить таблицу phone_types (Phone_Type TEXT (независимо от того, что), Priority INTEGER).В этой таблице перечислите каждое значение Phone_Type один раз и назначьте ему приоритет (в вашем примере, HOME будет 1, РАБОТА 2, ДРУГОЕ 3 и т. Д.).

Затем создайте представление, которое присоединяется к Приоритету.столбец из Phone_Types в вашу таблицу Phone_Numbers (представьте, что мы называем это Phone_Numbers_Ex).

Теперь у вас есть несколько вариантов получения записи из Phone_Numbers_Ex с MIN (Приоритетом) для данного emplID, из которых, вероятно, самый чистыйis:

SELECT * FROM Phone_Numbers_Ex P1 WHERE NOT EXISTS
   (SELECT * FROM Phone_Numbers_Ex P2 WHERE P2.EmplID = P1.EmplID AND P2.Priority < P1.Priority)

Другим способом является объявление другого представления или внутреннего запроса по линиям SELECT EmplID, MIN(Priority) AS Priority FROM Phone_Numbers_Ex GROUP BY EmplID и последующее присоединение этого обратного Phone_Numbers_Ex для EmplID и Priority.

2 голосов
/ 28 июня 2010

Я забыл, поддерживает ли Server 2000 Coalesce? Если это произойдет, я думаю, что это будет работать:

Select Distinct EmplID, Coalesce(
  (Select Phone from Employees where emplid = e1.emplid and phone_type = 'HOME'),
  (Select Phone from Employees where emplid = e1.emplid and phone_type = 'WORK'),
  (Select Phone from Employees where emplid = e1.emplid and phone_type = 'OTHER')
) as Phone
From Employees e1
0 голосов
/ 28 июня 2010

В качестве альтернативного ответа gddc, который использует запросы в предложении Select, вы можете использовать левые соединения.Вы можете получить лучшую производительность, но вы должны проверить, конечно.

SELECT
    e1.iD,
    Coalesce(phoneHome.Phone,phoneWork.Phone,phoneOther) phone
FROm
    employees e1
    LEFT JOIN phone phoneHome 
    ON e1.emplId = phoneHome 
        and phone_type = 'HOME'
    LEFT JOIN phone phoneWork 
    ON e1.emplId = phoneWork 
        and phone_type = 'WORK'
    LEFT JOIN phone phoneWork 
    ON e1.emplId = phoneOTHER
        and phone_type = 'OTHER'
0 голосов
/ 28 июня 2010

Ваши требования могут быть неполными, если сотруднику разрешено иметь более одного номера телефона для данного типа телефона.Я добавил phone_number_id только для того, чтобы сделать вещи уникальными, и предположил, что вам нужен самый низкий идентификатор, если у человека есть два телефона одного типа.Это довольно произвольно, но вы можете заменить его своей собственной бизнес-логикой.

Я также предположил, что существует какая-то таблица Phone_Types, которая включает ваш приоритет, для которого должен использоваться номер телефона.Если у вас еще нет этой таблицы, вам, вероятно, следует добавить ее.Если ничего другого, это позволяет вам ограничивать типы телефонов с помощью внешнего ключа.

SELECT
    PN1.employee_id,
    PN1.phone_type,
    PN1.phone_number
FROM
    Phone_Numbers PN1
INNER JOIN Phone_Types PT1 ON
    PT1.phone_type = PN1.phone_type
WHERE
    NOT EXISTS
    (
        SELECT *
        FROM
            Phone_Numbers PN2
        INNER JOIN Phone_Types PT2 ON
            PT2.phone_type = PN2.phone_type AND
            (
                (PT2.priority < PT1.priority)
--OR (PT2.priority = PT1.priority AND PN2.phone_number_id > PN1.phone_number_id)
            )
    )

Вы можете также реализовать это с помощью LEFT JOIN вместо NOT EXISTS, или вы можете использовать TOP, если вы ищетеномер телефона для одного сотрудника.Просто сделайте приоритет TOP 1 ORDER BY, phone_number_id.

Наконец, если вы хотите перейти на SQL 2005 или SQL 2008, вы можете использовать CTE с ROWNUMBER () OVER (приоритет ORDER BY, phone_number, PARTITIONBY employee_id) <- я думаю, что мой синтаксис может быть немного не таким, как в скобках, но, надеюсь, он достаточно понятен.Это позволит вам получить лучшую оценку для всех сотрудников, проверив, что ROWNUMBER () = 1. </p>

...