我有三个表TableA,TableB,TableC和Table A都有数百万条记录。
表A具有AccountId,表B具有accountId,客户及其证书,而表C具有证书。
情况是,表B中的AccountId具有多个具有多个证书的客户端。
当我尝试通过连接表B和C从表A中获取数据时,它会获取重复的记录,因为表B中的AccountId具有多个带有多个证书的客户端。
您可以使用此脚本填充表和数据以测试情况
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TableA]
(
[AccountId] [int] NOT NULL,
[Name] [nvarchar](50) NULL,
[Mobile] [nchar](10) NULL,
CONSTRAINT [PK_Accounts] PRIMARY KEY CLUSTERED
(
[AccountId] 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
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,
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
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TableC]
(
[CertificateId] [int] NOT NULL,
[Status] [bit] NOT NULL,
[Description] [nvarchar](50) NULL,
CONSTRAINT [PK_TableC] PRIMARY KEY CLUSTERED
(
[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].[TableA] ([AccountId], [Name], [Mobile]) VALUES (1, N'John', N'98 ')
INSERT [dbo].[TableA] ([AccountId], [Name], [Mobile]) VALUES (2, N'Henry', N'9808 ')
INSERT [dbo].[TableA] ([AccountId], [Name], [Mobile]) VALUES (3, N'Paine', N'9045 ')
INSERT [dbo].[TableA] ([AccountId], [Name], [Mobile]) VALUES (4, N'Andrew', N'887 ')
INSERT [dbo].[TableA] ([AccountId], [Name], [Mobile]) VALUES (5, N'Stocks', N'78 ')
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (1, 5, 34, 1)
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (2, 8, 34, 1)
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (3, 7, 36, 2)
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (4, 9, 37, 3)
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (5, 10, 37, 4)
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (6, 4, 37, 4)
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (7, 61, 37, 4)
INSERT [dbo].[TableB] ([Id], [ClientId], [CertificateId], [AccountId]) VALUES (8, 45, 35, 5)
INSERT [dbo].[TableC] ([CertificateId], [Status], [Description]) VALUES (34, 1, N'Certificate 1')
INSERT [dbo].[TableC] ([CertificateId], [Status], [Description]) VALUES (35, 1, N'Certificate 2')
INSERT [dbo].[TableC] ([CertificateId], [Status], [Description]) VALUES (36, 1, N'Certificate 3')
INSERT [dbo].[TableC] ([CertificateId], [Status], [Description]) VALUES (37, 0, N'Certificate 4')
ALTER TABLE [dbo].[TableB] WITH CHECK ADD CONSTRAINT [FK_TableB_TableA] FOREIGN KEY([AccountId])
REFERENCES [dbo].[TableA] ([AccountId])
GO
ALTER TABLE [dbo].[TableB] CHECK CONSTRAINT [FK_TableB_TableA]
GO
ALTER TABLE [dbo].[TableB] WITH CHECK ADD CONSTRAINT [FK_TableB_TableC] FOREIGN KEY([CertificateId])
REFERENCES [dbo].[TableC] ([CertificateId])
GO
ALTER TABLE [dbo].[TableB] CHECK CONSTRAINT [FK_TableB_TableC]
GO
我的查询
DECLARE @From int=1
DECLARE @To int=5
; WITH CTE_Data_WITH_PAGING AS
(SELECT ROW_NUMBER() OVER( ORDER BY A.AccountId) AS [ROW_NUMBERS],
A.AccountId, A.Name, A.Mobile FROM TableA A
LEFT JOIN TableB B
ON B.AccountId = A.AccountId
INNER JOIN TableC C
ON C.CertificateId=B.CertificateId
AND C.CertificateId<>01
)
SELECT * FROM CTE_Data_WITH_PAGING WHERE ROW_NUMBERS BETWEEN @From AND @To;
我尝试以这种方式使用与众不同的方法,但分页问题。
SELECT DISTINCT(AccountId), Name, Mobile
FROM CTE_Data_WITH_PAGING
WHERE ROW_NUMBERS BETWEEN @From AND @To;
答案 0 :(得分:3)
一种快速的解决方法是改用density_rank并结合使用distinct。这样可以确保每个帐户都被计数。还按照注释中的建议使用内部联接:
DECLARE @From int=1
DECLARE @To int=5
; WITH CTE_Data_WITH_PAGING AS
(SELECT distinct dense_rank() OVER( ORDER BY A.AccountId) AS [ROW_NUMBERS],
A.AccountId, A.Name, A.Mobile FROM TableA A
inner JOIN TableB B
ON B.AccountId = A.AccountId
INNER JOIN TableC C
ON C.CertificateId=B.CertificateId
AND C.CertificateId<>01
)
SELECT * FROM CTE_Data_WITH_PAGING WHERE ROW_NUMBERS BETWEEN @From AND @To;
我倾向于使用分组方式,而不是使用分组方式-尤其是与分析功能结合使用,因此使用row_number可能是:
DECLARE @From int=1
DECLARE @To int=5
; WITH CTE_Data_WITH_PAGING AS
(SELECT row_number() OVER( ORDER BY A.AccountId) AS [ROW_NUMBERS],
A.AccountId, A.Name, A.Mobile FROM TableA A
inner JOIN TableB B
ON B.AccountId = A.AccountId
INNER JOIN TableC C
ON C.CertificateId=B.CertificateId
AND C.CertificateId<>01
group by A.AccountId, A.Name, A.Mobile
)
SELECT * FROM CTE_Data_WITH_PAGING WHERE ROW_NUMBERS BETWEEN @From AND @To;
我在此处分组以获取唯一值。
最后,如果您只是在其中进行分页,并且不需要行号,则只需使用OFFSET和FETCH,如下所示:
DECLARE @From int = 1 宣告@To int = 5
SELECT
A.AccountId, A.Name, A.Mobile FROM TableA A
inner JOIN TableB B
ON B.AccountId = A.AccountId
INNER JOIN TableC C
ON C.CertificateId=B.CertificateId
AND C.CertificateId<>01
group by A.AccountId, A.Name, A.Mobile
order by A.AccountId
OFFSET (@FROM-1) ROWS FETCH NEXT (@TO-(@FROM-1)) ROWS ONLY;