使用无序字段作为条件,为每个员工选择一行

时间:2010-06-28 15:33:05

标签: sql sql-server-2000 greatest-n-per-group

我有一个如下所示的数据集。

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字段为每位员工选择一行来建立首选项。如果员工有一个,我想要家庭电话号码,如员工100和101的情况。如果不存在家庭电话号码,我想要工作号码(员工102),作为最后的手段,我会带其他与员工103一样的数字。实际上我的表有大约十几个PHONE_TYPE字段的值,所以我需要能够扩展任何解决方案,以包括不仅仅是我在示例中显示的三个值。有什么想法吗?感谢。

4 个答案:

答案 0 :(得分:3)

您需要添加phone_types表(Phone_Type TEXT(Whatever),Priority INTEGER)。在此表中,列出每个Phone_Type值一次并为其指定优先级(在您的示例中,HOME将为1,WORK 2,OTHER 3等等。)

然后,创建一个将优先级列从Phone_Types连接到Phone_Numbers表的视图(假设我们称之为Phone_Numbers_Ex)。

现在,您可以通过MIN(优先级)为给定的emplID从Phone_Numbers_Ex获取记录的几个选项,其中最明显的可能是:

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的行声明另一个视图或内部查询,然后在EmplID和Priority上加入后面的Phone_Numbers_Ex。

答案 1 :(得分:2)

我忘了,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

答案 2 :(得分:0)

如果允许员工拥有指定电话类型的多个电话号码,则您的要求可能不完整。我添加了一个phone_number_id只是为了让事情变得独一无二,并假设如果这个人有两个相同类型的手机,你会想要最低的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,phone_number_id。

最后,如果你要升级到SQL 2005或SQL 2008,你可以使用带有ROWNUMBER()OVER(ORDER BY priority,phone_number,PARTITION BY employee_id)的CTE&lt; - 我想我的语法可能略有偏差用括号括起来,但希望它足够清楚。这将允许您通过检查ROWNUMBER()= 1来获得所有员工的最高职位。

答案 3 :(得分:0)

作为替代g.d.d.c在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'