我有 2 个表 - 一个帐户表和一个用户表。每个帐户可以有多个用户。我有一个场景,我想对这两个表执行单个查询/联接,但我想要所有帐户数据 (Account.*),并且只需要 第一的 用户数据集(特别是他们的名字)。

我不想对聚合组执行“最小”或“最大”操作,而是想做“第一个”操作。但是,显然,TSQL 中没有“第一个”聚合函数。

关于如何获取此查询有什么建议吗?显然,很容易得到Account x Users的笛卡尔积:

 SELECT User.Name, Account.* FROM Account, User
 WHERE Account.ID = User.Account_ID

但是我如何才能根据 User.ID 的顺序仅从产品中获取第一个用户呢?

有帮助吗?

解决方案

而不是分组,去做这样的...

select
    *

from account a

join (
    select 
        account_id, 
        row_number() over (order by account_id, id) - 
            rank() over (order by account_id) as row_num from user
     ) first on first.account_id = a.id and first.row_num = 0

其他提示

我知道我的回答有点晚了,但这可能对其他人有帮助。有一种方法可以在 SQL Server 中实现 First() 和 Last(),如下所示:

Stuff(Min(Convert(Varchar, DATE_FIELD, 126) + Convert(Varchar, DESIRED_FIELD)), 1, 23, '')

对 First() 使用 Min(),对 Last() 使用 Max()。DATE_FIELD 应该是确定它是第一条记录还是最后一条记录的日期。DESIRED_FIELD 是您想要第一个或最后一个值的字段。它的作用是:

  1. 在字符串开头添加 ISO 格式的日期(23 个字符长)
  2. 将 DESIRED_FIELD 附加到该字符串
  3. 获取该字段的最小/最大值(因为它以日期开头,您将获得第一条或最后一条记录)
  4. 连接字符串以删除前 23 个字符(日期部分)的内容

干得好!

编辑:我对第一个公式有问题:当 DATE_FIELD 的毫秒数为 0.000 时,SQL Server 将日期作为字符串返回,根本没有毫秒,从而从 DESIRED_FIELD 中删除前 4 个字符。我只是将格式更改为“20”(没有毫秒),效果很好。唯一的缺点是,如果您有两个在同一秒创建的字段,则排序可能会很混乱......在这种情况下,您可以将格式恢复为“126”。

Stuff(Max(Convert(Varchar, DATE_FIELD, 20) + Convert(Varchar, DESIRED_FIELD)), 1, 19, '')

编辑2:我的初衷是返回最后一个(或第一个)非空行。有人问我如何返回最后一行或第一行,无论它是否为空。只需将 ISNULL 添加到 DESIRED_FIELD 即可。当您使用 + 运算符连接两个字符串时,当其中一个为 NULL 时,结果为 NULL。所以使用以下内容:

Stuff(Max(Convert(Varchar, DATE_FIELD, 20) + IsNull(Convert(Varchar, DESIRED_FIELD), '')), 1, 19, '')
Select *
From Accounts a
Left Join (
    Select u.*, 
    row_number() over (Partition By u.AccountKey Order By u.UserKey) as Ranking
    From Users u
  ) as UsersRanked
  on UsersRanked.AccountKey = a.AccountKey and UsersRanked.Ranking = 1

这可以通过使用PARTITION BY子句被简化。在上文中,如果帐户有三个用户,然后该子查询他们的数字1,2和3,而对于不同AccountKey,它将复位numnbering。这意味着对每个唯一AccountKey,总是会有一个1,和潜在的2,3,4等

因此,你上排名= 1过滤抓住从各组的第一个。

这会给你一个行每个帐户,如果有该帐户至少一个用户,然后它会给你用最低的关键用户(因为我用的是左连接,你总是会得到一个帐号上市即使没有用户存在)。如果你喜欢的第一用户按字母顺序选择或一些其它标准与另一个字段替换Order By u.UserKey

这星古莱特的STUFF响应是光滑。但是,如果你的DATE_FIELD是SMALLDATETIME(而不是DATETIME),然后是ISO 8601的长度将是19而不是23(因为SMALLDATETIME没有毫秒) - 所以相应地调整STUFF参数或从STUFF函数的返回值将是不正确的(失踪的前四个字符)。

第一个和最后不要在SQL Server 2005或2008的存在,但是在SQL Server 2012中有一个FIRST_VALUE,LAST_VALUE函数。我试图执行总第一个和最后对于SQL Server 2005,来到该SQL Server会保证在规定的顺序汇总计算的障碍。 (见属性SqlUserDefinedAggregateAttribute.IsInvariantToOrder属性,这是没有实现)。这可能是因为查询分析器试图执行多个线程上的总的计算和合并结果,从而加快了执行,但不保证的顺序这些元件被聚集。

您可以使用OUTER APPLY,请参见文档

SELECT User1.Name, Account.* FROM Account
OUTER APPLY 
    (SELECT  TOP 1 Name 
    FROM [User]
    WHERE Account.ID = [User].Account_ID
    ORDER BY Name ASC) User1

我为基准的所有方法,实现这一目标的simpelest和最快的方法是通过使用外/交叉应用

SELECT u.Name, Account.* FROM Account
OUTER APPLY (SELECT TOP 1 * FROM User WHERE Account.ID = Account_ID ) as u

CROSS APPLY作品就像INNER JOIN,并获取两个表相关的行,而OUTER APPLY作品像LEFT OUTER JOIN和左表(此帐户)获取所有行

SELECT (SELECT TOP 1 Name 
        FROM User 
        WHERE Account_ID = a.AccountID 
        ORDER BY UserID) [Name],
       a.*
FROM Account a

有许多这样做,这里一个快速和肮脏的一个方式。

Select (SELECT TOP 1 U.Name FROM Users U WHERE U.Account_ID = A.ID) AS "Name,
    A.*
FROM Account A

定义 “第一”。你认为的第一件事就是通常具有聚集索引顺序做,但不应该在依赖(你能凑合是打破它的例子)一个巧合。

您是对不使用MAX()或MIN()。虽然诱人,考虑你的姓氏和名字都在不同的领域的情况。你可能会得到不同的记录名。

由于它听起来像所有真正关心的是,你只有一个任意的记录为一组,你可以做什么是公正的MIN或MAX该记录的ID字段,然后加入表插入该ID查询

(稍微偏离主题,但)我经常跑集合查询列出例外摘要,然后我想知道为什么一个客户是结果,所以使用MIN和MAX给2半随机样品,我可以看在细节,例如

SELECT Customer.Id, COUNT(*) AS ProblemCount
      , MIN(Invoice.Id) AS MinInv, MAX(Invoice.Id) AS MaxInv
FROM Customer
INNER JOIN Invoice on Invoice.CustomerId = Customer.Id
WHERE Invoice.SomethingHasGoneWrong=1
GROUP BY Customer.Id

创建和子查询“FirstUser”返回第一用户对于每个帐户加入

SELECT User.Name, Account.* 
FROM Account, User, 
 (select min(user.id) id,account_id from User group by user.account_id) as firstUser
WHERE Account.ID = User.Account_ID 
 and User.id = firstUser.id and Account.ID = firstUser.account_id
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top