Использование CTE с подкачкой для извлечения записей из 3 таблиц - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть три таблицы TableA, TableB, TableC и Table A, содержащие миллионы записей.

Таблица A имеет AccountId, таблица B имеет accountId, Client и их сертификат, а TableC имеет сертификаты.

Ситуация такова, что в таблице B AccountId есть несколько клиентов с несколькими сертификатами.

Когда я пытаюсь извлечь данные из таблицы A путем объединения таблиц B и C, он выбирает дубликаты записей, поскольку в таблице 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;

enter image description here

Я пытаюсь использовать разные таким образом, но проблема подкачки.

SELECT DISTINCT(AccountId), Name, Mobile 
FROM CTE_Data_WITH_PAGING 
WHERE ROW_NUMBERS BETWEEN @From AND @To;

Проблема подкачки: попробуйте @ from = 1 @ to = 4 и посмотрите вывод.Должен получить AccountId: 1, 2, 3,4 enter image description here

1 Ответ

0 голосов
/ 21 декабря 2018

Быстрое решение заключается в использовании вместо этого diff_rank в сочетании с различными.Это гарантирует, что каждая учетная запись считается.также используйте внутреннее объединение, как рекомендовано в комментариях:

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;

Я предпочитаю использовать group by вместо отдельных - особенно в сочетании с аналитическими функциями, поэтому другой вариант - использование 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, какthis:

DECLARE @From int = 1 DECLARE @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;
...