代码之家  ›  专栏  ›  技术社区  ›  Justin808

如何在SQL Server中将左连接限制为第一个结果?

  •  19
  • Justin808  · 技术社区  · 15 年前

    我有一点SQL,几乎是做我想做的事。我正在使用三个表,一个是Users,UserPhoneNumbers和UserPhoneNumberTypes。我想得到一份用户名单,上面有他们的电话号码。

    如何修改SQL以获取列出的第一个电话号码而忽略其余号码?我是SQL Server的,我知道TOP语句。但是如果我在左JOIN select语句中添加'TOP 1',它只会给我数据库中的第一个条目,而不是每个用户的第一个条目。

    这是针对SQL Server 2000的。

    谢谢,

    SELECT  Users.UserID, 
      Users.FirstName, Users.LastName,
      HomePhone, WorkPhone, FaxNumber
    
    FROM Users
    
    LEFT JOIN
     (SELECT UserID, PhoneNumber AS HomePhone
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Home') AS tmpHomePhone
     ON tmpHomePhone.UserID = Users.UserID
    LEFT JOIN
     (SELECT UserID, PhoneNumber AS WorkPhone
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Work') AS tmpWorkPhone
     ON tmpWorkPhone.UserID = Users.UserID
    LEFT JOIN
     (SELECT UserID, PhoneNumber AS FaxNumber
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Fax') AS tmpFaxNumber
     ON tmpFaxNumber.UserID = Users.UserID
    
    8 回复  |  直到 15 年前
        1
  •  6
  •   Dan J    15 年前

    由于SQL Server 2000和排名函数已退出,您可以使子查询选择聚合:

    SELECT UserID, MAX(PhoneNumber) AS HomePhone FROM [...] GROUP BY UserID
    

        2
  •  7
  •   OMG Ponies    15 年前

    假设SQL Server 2005+,请使用行号:

    LEFT JOIN (SELECT UserID, 
                      PhoneNumber AS HomePhone,
                      ROW_NUMBER() OVER (PARTITION BY userid ORDER BY what?) AS rank
                 FROM UserPhoneNumbers  upn
            LEFT JOIN UserPhoneNumberTypes upnt ON upnt.UserPhoneNumberTypeID = upn.UserPhoneNumberTypeID
                                               AND upnt.PhoneNumberType='Home') AS tmpHomePhone
                    ON tmpHomePhone.UserID = Users.UserID
                   AND tmpHomePhone.rank = 1
    

    当心点 what?

        3
  •  7
  •   Remus Rusanu    15 年前

    每当您只想从左表中选择顶行时 每行 在右边的表中,您应该考虑使用APPLY操作符而不是join,并移动join条件 里面 左连接:

    SELECT  u.UserID, 
      u.FirstName, u.LastName,
      hn.PhoneNumber AS HomePhone
    FROM Users u
    OUTER APPLY (
     SELECT TOP(1) PhoneNumber 
     FROM UserPhoneNumbers upn
     LEFT JOIN UserPhoneNumberTypes upt 
       ON upn.UserPhoneNumberTypeID=upt.UserPhoneNumberTypeID
     WHERE upt.PhoneNumberType='Home'
     AND upn.UserID = u.UserID
     ORDER BY ...) as hn
    ...
    
        4
  •  1
  •   Thomas    15 年前
    Select Users.UserID,  Users.FirstName, Users.LastName
        , PhoneNumbers.HomePhone
        , PhoneNumbers.WorkPhone
        , PhoneNumbers.FaxNumber
    From Users
        Left Join   (
                    Select UPN.UserId
                        , Min ( Case When PN.PhoneNumberType = 'Home' Then UPN.PhoneNumber End ) As HomePhone
                        , Min ( Case When PN.PhoneNumberType = 'Work' Then UPN.PhoneNumber End ) As WorkPhone
                        , Min ( Case When PN.PhoneNumberType = 'Fax' Then UPN.PhoneNumber End ) As FaxPhone
                    From UserPhoneNumbers As UPN
                            Join    (
                                    Select Min(UPN1.UserPhoneNumberId) As MinUserPhoneNumberId
                                        , UPNT1.PhoneNumberType
                                    From UserPhoneNumbers As UPN1
                                        Join UserPhoneNumberTypes As UPNT1
                                            On UPNT1.UserPhoneNumberTypeID = UPN1.UserPhoneNumberTypeID
                                    Where UPNT1.PhoneNumberType In('Home', 'Work', 'Fax')
                                    Group By UPN1.UserID, UPNT.PhoneNumberType
                                    ) As PN
                                On PN.MinUserPhoneNumberId = UPN.UserPhoneNumberId
                    Group By UPN.UserId
                    ) As PhoneNumbers
        On PhoneNumbers.UserId = Users.UserId
    

    在这个解决方案中,对于每个用户和电话号码类型,我从 UserPhoneNumbers 表(我猜列的名称是 UserPhoneNumberId ).

        5
  •  1
  •   Marcus Adams    15 年前

    SELECT  Users.UserID, Users.FirstName, Users.LastName, hp.HomePhone,
            wp.WorkPhone, fn.FaxNumber
    FROM Users
    LEFT JOIN HomePhone hp ON hp.UserID = Users.UserID
    LEFT JOIN HomePhone hp2 ON hp2.UserID = Users.UserID AND hp2.ID < hp.ID
    LEFT JOIN WorkPhone wp ON wp.UserID = Users.UserID
    LEFT JOIN WorkPhone wp2 ON wp2.UserID = Users.UserID AND wp2.ID < wp.ID
    LEFT JOIN FaxNumber fn ON fn.UserID = Users.UserID
    LEFT JOIN FaxNumber fn2 ON fn2.UserID = Users.UserID AND fn2.ID < fn.ID
    WHERE hp2.ID IS NULL AND wp2.ID IS NULL AND fn2.ID IS NULL
    

    SQL Antipatterns .

        6
  •  0
  •   Joel Coehoorn    15 年前

    当有两个相同类型的数字时,您必须定义“first”的含义,然后向联接添加一个条件,以便只有正确的记录符合条件。没有别的捷径了。

        7
  •  0
  •   Frank    15 年前

    SELECT  Users.UserID, 
      Users.FirstName, Users.LastName,
      HomePhone, WorkPhone, FaxNumber
    
    FROM Users
    
    LEFT JOIN
     (SELECT UserID, min(PhoneNumber) AS HomePhone
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Home'
     GROUP BY userID) AS tmpHomePhone
     ON tmpHomePhone.UserID = Users.UserID
    LEFT JOIN
     (SELECT UserID, min(PhoneNumber) AS WorkPhone
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Work'
     GROUP BY userID) AS tmpWorkPhone
     ON tmpWorkPhone.UserID = Users.UserID
    LEFT JOIN
     (SELECT UserID, min(PhoneNumber) AS FaxNumber
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Fax'
     GROUP BY userID) AS tmpFaxNumber
     ON tmpFaxNumber.UserID = Users.UserID
    

    除了min(),还可以使用max()。

    或者你可以在一个小组里做:

    SELECT  Users.UserID, 
      Users.FirstName, Users.LastName,
      max(HomePhone) as HomePhone,
      max(WorkPhone) as WorkPhone,
      max(FaxNumber) as FaxNumber
    
    FROM Users
    
    LEFT JOIN
     (SELECT UserID, PhoneNumber AS HomePhone
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Home') AS tmpHomePhone
     ON tmpHomePhone.UserID = Users.UserID
    LEFT JOIN
     (SELECT UserID, PhoneNumber AS WorkPhone
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Work') AS tmpWorkPhone
     ON tmpWorkPhone.UserID = Users.UserID
    LEFT JOIN
     (SELECT UserID, PhoneNumber AS FaxNumber
     FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
     WHERE UserPhoneNumberTypes.PhoneNumberType='Fax') AS tmpFaxNumber
     ON tmpFaxNumber.UserID = Users.UserID
    
        8
  •  0
  •   Gowri    12 年前

    等等,我只是想了解这个问题。

    你有两张桌子:

    用户(UserID-->x) 用户电话(用户ID、电话类型-->电话号码) 而且UserID/PhoneType不是唯一的。

    首先,不需要临时表:

    Select 
     x
    from
     Users
    inner join 
     (
       Select 
        top 1 y
       from
        FoneTypes
       where
        UserID = users.UseriD
       and phoneType = 'typex'
     ) as PhoneTypex on phonetypex.UserID = users.userID
    

    还是我遗漏了什么?