如果所需的列值重复 [重复],则 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)
上建立索引以提高性能。
【讨论】:
不应该max
是min
并且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 查询以获取顶部记录的主要内容,如果未能解决你的问题,请参考以下文章
R - 如果列值与字符向量中的任何值匹配,则返回它旁边的列 [重复]