SQL Server 2008 R2: удаление повторяющихся строк из таблиц, содержащих в представлении

- Создание таблицы dup1

CREATE TABLE dup1
(
    cola VARCHAR(10),
    colb VARCHAR(10)
);

- Вставка записей

INSERT INTO dup1 VALUES('1','2');
INSERT INTO dup1 VALUES('1','2');
INSERT INTO dup1 VALUES('1','3');
INSERT INTO dup1 VALUES('1','4');
INSERT INTO dup1 VALUES('1','5');

- Создание таблицы dup2

CREATE TABLE dup2
(
    cola VARCHAR(10),
    colb VARCHAR(10)
);

- Вставка записей

INSERT INTO dup2 VALUES('1','2');
INSERT INTO dup2 VALUES('1','2');
INSERT INTO dup2 VALUES('2','3');
INSERT INTO dup2 VALUES('2','4');
INSERT INTO dup2 VALUES('1','5');

- Создание представления

CREATE VIEW V_Dup as
SELECT * FROM dup1 UNION ALL 
SELECT * FROM dup2;

Примечание. Записи 1,2 и 1,5 дублируются в обеих таблицах НО хотят сохранить исходные данные.

Мой вопрос заключается в том, как удалить дубликаты записей из обеих таблиц.

Ответ 1

создать темп. таблица:

DECLARE @tempDuplicateTable AS TABLE(
    cola VARCHAR(10),
    colb VARCHAR(10)
)

вставить строку дубликата:

INSERT INTO @tempDuplicateTable
    ( cola, colb )
    (
        SELECT a.cola, a.colb FROM dup1 a
        INNER JOIN dup2 b ON b.cola = a.cola AND b.colb = a.colb
    )

удалить повторяющиеся данные из обеих таблиц dup1 и dup2:

DELETE a FROM dup1 a INNER JOIN @tempDuplicateTable b ON b.cola = a.cola AND b.colb = a.colb
DELETE a FROM dup2 a INNER JOIN @tempDuplicateTable b ON b.cola = a.cola AND b.colb = a.colb

если вы просто хотите получить этот результат:

 cola   colb
   1    2
   1    3
   1    4
   1    5
   2    3
   2    4

попробуйте этот запрос:

SELECT DISTINCT * FROM V_Dup 

или вы можете изменить свой вид следующим образом:

CREATE VIEW V_Dup as
    SELECT DISTINCT a.* FROM (
    SELECT * FROM dup1 UNION ALL 
    SELECT * FROM dup2
) a

Ответ 2

Создайте временную таблицу, в которой строки должны быть удалены из CTE, и удалите их из обеих таблиц.

Query

CREATE TABLE dup1
(
    cola VARCHAR(10),
    colb VARCHAR(10)
);

INSERT INTO dup1 VALUES('1','2');
INSERT INTO dup1 VALUES('1','2');
INSERT INTO dup1 VALUES('1','3');
INSERT INTO dup1 VALUES('1','4');
INSERT INTO dup1 VALUES('1','5');

CREATE TABLE dup2
(
    cola VARCHAR(10),
    colb VARCHAR(10)
);

INSERT INTO dup2 VALUES('1','2');
INSERT INTO dup2 VALUES('1','2');
INSERT INTO dup2 VALUES('2','3');
INSERT INTO dup2 VALUES('2','4');
INSERT INTO dup2 VALUES('1','5');

CREATE VIEW V_Dup as
SELECT * FROM dup1 UNION ALL
SELECT * FROM dup2;

;with cte as 
(
    select rn=row_number() over
    (
        partition by cola,colb
        order by cola,colb
    ),*
    from V_Dup
)
select * into #temp
from cte 
where rn>1;

delete t1 from dup1 t1
inner join  #temp t2
on t1.cola = t2.cola
and t1.colb = t2.colb;

delete t1 from dup2 t1
inner join  #temp t2
on t1.cola = t2.cola
and t1.colb = t2.colb;

drop table #temp;

Ответ 3

Я думаю, вы хотите просто увидеть запись один раз в своем представлении, потому что вы говорите, что хотите сохранить исходные данные. Таким образом, вы должны использовать UNION вместо UNION ALL в вашем представлении

CREATE VIEW V_Dup as
SELECT * FROM dup1 UNION  
SELECT * FROM dup2;

В противном случае, если вы хотите удалить все дублированные строки из представления создания таблицы, вам нужно будет сделать что-то вроде этого:

;WITH DUP_CTE AS
(
SELECT  cola, colb,ROW_NUMBER() OVER (PARTITION BY cola,colb ORDER BY (SELECT 0)) RN FROM V_Dup 
)
DELETE FROM DUP_CTE

WHERE EXISTS( SELECT 0 FROM dup_cte AS c WHERE c.cola=dup_cte.cola AND c.colb=dup_cte.colb AND RN <> 1);

Если вы хотите удалить только дубликаты:

;WITH DUP_CTE AS
    (
    SELECT  cola, colb,ROW_NUMBER() OVER (PARTITION BY cola,colb ORDER BY (SELECT 0)) RN FROM V_Dup 
    )
    DELETE FROM DUP_CTE
    WHERE  RN <> 1;

Но для последних двух решений вам понадобится первичный ключ в ваших таблицах.

Ответ 4

Являются ли таблицы раздельного доступа dup1 и dup2? Я имею в виду, если вы можете добавить к ним столбец, который позволяет понять, какая таблица обновляется. например date, чтобы, если вы вставляете дату с интервалом, вы обновляете dup1 else dup2. Если у вас нет столбца секционирования, вы можете создать его, просто добавив столбец, способный идентифицировать таблицу (например, varchar (1) со значением "1" или int, имеющее значение 1 для dup1 и 2 для dup2). Этот столбец должен быть частью первичного ключа (здесь я создал идентификатор). Таблица может выглядеть так:

CREATE TABLE dbo.dup1
    (
    cola   VARCHAR (10),
    colb   VARCHAR (10),
    ID     INT IDENTITY NOT NULL,
    partit NCHAR (10) CONSTRAINT DF_dup1_partit DEFAULT ('1') NOT NULL CONSTRAINT CK_dup1 CHECK ([PARTIT]='1'),
    CONSTRAINT PK_dup1 PRIMARY KEY (ID, partit)
    )

CREATE TABLE dbo.dup2
    (
    cola   VARCHAR (10),
    colb   VARCHAR (10),
    ID     INT IDENTITY NOT NULL,
    partit NCHAR (10) CONSTRAINT DF_dup2_partit DEFAULT ('2') NOT NULL CONSTRAINT CK_dup2 CHECK ([PARTIT]='2'),
    CONSTRAINT PK_dup2 PRIMARY KEY (ID, partit)
    )

Таким образом, представление будет выглядеть следующим образом:

CREATE VIEW V_Dup as
SELECT * FROM dup1 UNION ALL 
SELECT * FROM dup2
WITH CHECK OPTION

таким образом вы сможете использовать код, который я разместил до