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

如何在SQL Server中将左连接限制为第一个结果?,sql,sql-server,sql-server-2000,greatest-n-per-group,Sql,Sql Server,Sql Server 2000,Greatest N Per Group,我有一点SQL,它几乎完成了我想要它做的事情。我正在使用三个表,一个是Users,UserPhoneNumbers和UserPhoneNumberTypes。我正在尝试获取一个用户列表,其中包含他们的电话号码,以便进行导出 数据库本身很旧,存在一些完整性问题。我的问题是数据库中每个电话号码应该只有一种类型,但事实并非如此。当我运行这个程序时,如果每个人都包含两个“家庭”号码,我会得到多行结果 如何修改SQL以获取列出的第一个电话号码并忽略其余号码?我是SQL Server的,我知道TOP语句。但

我有一点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

当有两个相同类型的数字时,必须定义“first”的含义,然后向联接添加一个条件,以便只有正确的记录符合条件。没有其他快捷方式可用于此操作。

假设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

注意
什么?
占位符以确定第一个数字。如果您根本不在乎,请通过忽略顺序…

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

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

如果您不关心返回哪个用户的家庭号码…

我假设您在每个连接的表上都有一些主键字段,因为UserID不是唯一的。我假设您的主键名为ID。我们将获取ID最低的记录。这符合您的“第一”标准

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
选择Users.UserID、Users.FirstName、Users.LastName、hp.HomePhone、,
wp.WorkPhone,fn.FaxNumber
来自用户
在hp.UserID=Users.UserID上左键加入HomePhone hp
在hp2.UserID=Users.UserID和hp2.ID

书中有一整章是关于这类问题的,叫做“含糊不清的咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜咕噜

您有两张桌子:

用户(用户标识-->x)用户电话(用户标识,电话类型-->电话号码) 而且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
根据需要添加内部联接


还是我遗漏了什么?

您可以使用GROUP BY:

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
也可以使用max()代替min()

或者您可以通过以下方式在一组中完成:

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

在这个解决方案中,对于每个用户和电话号码类型,我从
UserPhoneNumbers
表中选择最低的主键值(我猜该列名为
UserPhoneNumberId

每当您想从右表中的每一行中只从左表中选择顶行时,应考虑使用Apple运算符而不是联接,并在左联接中移动联接条件:

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
...

这取决于您所谈论的SQL的版本。如果是SQL Server 2005+,您有许多选项,包括排名查询。解决方案是[here][1],只需将join替换为left join即可。[1] :First-select语句返回的第一行。表中没有其他标准限制设置的结果。好的,我想我现在更了解你了。。。电话桌上有ID字段吗?你也许可以在max(id)上加入。我知道我以前也做过类似的事情。这些不是真正的临时表,只是子查询,但你是对的,你不需要它们。这非常简单,我经常使用。谢谢,对我来说,结果数字不是问题,所以只有一个。这在MS SQL 2000中有效吗?不,仅限SQL 2005及其后版本。我想你只要求SQL 2K。
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
...