如果所需的列值重复 [重复],则 SQL 查询以获取顶部记录

Posted

技术标签:

【中文标题】如果所需的列值重复 [重复],则 SQL 查询以获取顶部记录【英文标题】:SQL query to fetch top record if the desired column value is duplicated [duplicate] 【发布时间】:2019-05-21 10:17:09 【问题描述】:

在下表中,主键是:Id、ClientId、CertificateId。 AccountId 列是重复的,因为 AccountId 可能有不同的客户,而客户可能有不同的证书。 下面是用于操作表和数据的脚本。

SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[TableB](
        [Id] [int] NOT NULL,
        [ClientId] [int] NOT NULL,
        [CertificateId] [int] NOT NULL,
        [AccountId] [int] NOT NULL,
        [Status] [bit] NULL,
     CONSTRAINT [PK_TableB] PRIMARY KEY CLUSTERED 
    (
        [Id] ASC,
        [ClientId] ASC,
        [CertificateId] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (1, 5, 34, 1, 1)
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (2, 8, 34, 1, 1)
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (3, 7, 36, 2, 1)
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (4, 9, 37, 3, 1)
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (5, 10, 35, 4, 1)
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (6, 4, 37, 4, 0)
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (7, 61, 34, 4, 1)
    INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId], [Status]) VALUES (8, 45, 35, 5, 1)

    GO

当我执行这个查询时:SELECT * FROM TableB WHERE [Status]=1 我得到以下结果

Id  |  ClientId |  CertificateId  | AccountId | Status
1   |   5       |      34         |     1     |   1
2   |   8       |      34         |     1     |   1
3   |   7       |      36         |     2     |   1
4   |   9       |      37         |     3     |   1
5   |   10      |      35         |     4     |   1
7   |   61      |      34         |     4     |   1
8   |   45      |      35         |     5     |   1

请帮助我得到以下结果,意味着如果任何 AccountId 重复然后选择前 1 并且输出应该是这样的

Id  |  ClientId |  CertificateId  | AccountId | Status
1   |   5       |      34         |     1     |   1
3   |   7       |      36         |     2     |   1
4   |   9       |      37         |     3     |   1
5   |   10      |      35         |     4     |   1
8   |   45      |      35         |     5     |   1

【问题讨论】:

select * from (select *, RN = row_number() over (partition by AccountId order by Id) from TableB where [Status] = 1) x where RN = 1 【参考方案1】:

通常最快的方法使用相关子查询:

SELECT b.*
FROM TableB b
WHERE b.id = (SELECT MIN(b2.id)
              FROM tableB b2
              WHERE b2.Status = 1 AND
                    b2.AccountId = b.AccountId
             );

您希望在tableB(AccountId, status, id) 上建立索引以提高性能。

【讨论】:

不应该maxmin 并且where 子句也在子查询中? 当有百万条记录时,这个查询是否比上面的 CTE 查询执行得更快 @scsimon 。 . . max() 应该是min() 并且对status 的过滤只需要在子查询中,而不需要在外部查询中。 @user3803537 。 . .带有 CTE 的查询使用 ROW_NUMBER()。我认为这个版本在正确的索引下会更快。 好点。只需要内查询【参考方案2】:

看看下面的查询。请注意,我使用分区来获得分组的 row_numbers (rn) ,然后使用 rn 过滤查询

WITH t AS
(
   SELECT *,
         ROW_NUMBER() OVER (PARTITION BY AccountId ORDER BY Id Asc) AS rn
   FROM TableB WHERE [Status]=1
)
SELECT *
FROM t
WHERE rn = 1

【讨论】:

不应该是ORDER BY Id ASC吗?但同样,这是一个重复的问题。 是的,很好 :) 标记到@TheImpaler,您还需要在该CTE 中使用WHERE 子句来说明STATUS,否则STATUS != 1 可能会导致rn = 1【参考方案3】:

看起来您希望每个帐户有一条记录,然后是其他字段的最小值。如果是这样,只需像这样按帐户分组:

SELECT
Min(Id) Id,
Min(ClientId) ClientId,
Min(CertificateId) CertificateId,
AccountId,
Status
FROM TableB
WHERE Status=1
GROUP BY AccountId, Status

【讨论】:

这假设 ClientId 和 CertificateId 总是会增加,正如您所看到的,也不会增加 不,它没有。即使 ClientID 重复,查询也只会获取 Min。似乎正是问题要求使用基本 sql(分区也可以,但这是更高级的 sql) 但是MIN 可能不是第一个实例,即最高记录...因此MIN 会产生不正确的结果。详细地说,您的 Id、ClientID 和 CertificateId 都可能来自不同的行......这不是 OP 想要的。

以上是关于如果所需的列值重复 [重复],则 SQL 查询以获取顶部记录的主要内容,如果未能解决你的问题,请参考以下文章

sql优化

R - 如果列值与字符向量中的任何值匹配,则返回它旁边的列 [重复]

SQL返回重复的列值

如果条件满足,则更新表。 (将表b中的值更新为a)所需的相应值[重复]

SQL优化以及索引

为每个不同的列值选择第一行 [重复]