Как удалить дубликаты строк в sybase, если у вас нет уникального ключа?
Да, вы можете найти подобные вопросы много раз, но: самые элегантные решения, размещенные здесь, работают для SQL Server, но не для Sybase (в моем случае Sybase Anywhere 11). Я даже нашел некоторые вопросы, связанные с Sybase, помеченные как дубликаты для вопросов SQL Server, что не помогает.
Одним из примеров решений, которые мне понравились, но не сработали, является конструкцияWITH ... DELETE ...
.
Я нашел рабочие решения, используя курсоры или циклы while, но я надеюсь, что это возможно без петли.
Я надеюсь на хороший, простой и быстрый запрос, просто удалив все, кроме одного точного дубликата.
Вот небольшой фреймворк для тестирования:
IF OBJECT_ID( 'tempdb..#TestTable' ) IS NOT NULL
DROP TABLE #TestTable;
CREATE TABLE #TestTable (Column1 varchar(1), Column2 int);
INSERT INTO #TestTable VALUES ('A', 1);
INSERT INTO #TestTable VALUES ('A', 1); -- duplicate
INSERT INTO #TestTable VALUES ('A', 1); -- duplicate
INSERT INTO #TestTable VALUES ('A', 2);
INSERT INTO #TestTable VALUES ('B', 1);
INSERT INTO #TestTable VALUES ('B', 2);
INSERT INTO #TestTable VALUES ('B', 2); -- duplicate
INSERT INTO #TestTable VALUES ('C', 1);
INSERT INTO #TestTable VALUES ('C', 2);
SELECT * FROM #TestTable ORDER BY Column1,Column2;
DELETE <your solution here>
SELECT * FROM #TestTable ORDER BY Column1,Column2;
5 ответов:
Если все поля идентичны, вы можете просто сделать это:
select distinct * into #temp_table from table_with_duplicates delete table_with_duplicates insert into table_with_duplicates select * from #temp_table
Если все поля не идентичны, например, если у вас есть id, который отличается, то вам нужно будет перечислить все поля в инструкции select и жестко закодировать значение в id, чтобы сделать его идентичным, если это поле вас не волнует. Например:
insert #temp_table field1, field2, id select (field1, field2, 999) from table_with_duplicates
Это работает хорошо и быстро:
Если вы не знаетеDELETE FROM #TestTable WHERE ROWID(#TestTable) IN ( SELECT rowid FROM ( SELECT ROWID(#TestTable) rowid, ROW_NUMBER() OVER(PARTITION BY Column1,Column2 ORDER BY Column1,Column2) rownum FROM #TestTable ) sub WHERE rownum > 1 );
OVER(PARTITION BY ...)
, просто выполните внутренний операторSELECT
, чтобы увидеть, что он делает.
Вот еще один интересный пример, который я нашел и принял:
DELETE FROM #TestTable dupes FROM #TestTable dupes, #TestTable fullTable WHERE dupes.Column1 = fullTable.Column1 AND dupes.Column2 = fullTable.Column2 AND ROWID(dupes) > ROWID(fullTable);
Или, если вам больше нравятся явные соединения (я это делаю):
DELETE FROM #TestTable dupes FROM #TestTable dupes INNER JOIN #TestTable fullTable ON dupes.Column1 = fullTable.Column1 AND dupes.Column2 = fullTable.Column2 AND ROWID(dupes) > ROWID(fullTable);
Или короткая форма ("естественное" соединение автоматически включает идентичные имена столбцов):
DELETE FROM #TestTable dupes FROM #TestTable dupes NATURAL JOIN #TestTable fullTable ON ROWID(dupes) > ROWID(fullTable);
...если кто-то находит решение, не требующее
ROWID()
, мне было бы интересно посмотреть на них.
Пожалуйста, попробуйте это:
create clustered index i1 on table table_name(column_name) with ignore_dup_row
create table #test(id int,name char(9)) insert into #test values(1,"A") insert into #test values(1,"A") create clustered index i1 on #test(id) with ignore_dup_row select * from #test
Хорошо, теперь, когда я знаю функцию
ROWID()
, решения для таблиц с первичным ключом (PK) могут быть легко приняты. Сначала он выбирает все строки для сохранения, а затем удаляет оставшиеся:DELETE FROM #TestTable FROM #TestTable LEFT OUTER JOIN ( SELECT MIN(ROWID(#TestTable)) rowid FROM #TestTable GROUP BY Column1, Column2 ) AS KeepRows ON ROWID(#TestTable) = KeepRows.rowid WHERE KeepRows.rowid IS NULL;
...или как насчет этого более короткого варианта? Мне нравится!
DELETE FROM #TestTable WHERE ROWID(#TestTable) NOT IN ( SELECT MIN(ROWID(#TestTable)) FROM #TestTable GROUP BY Column1, Column2 );
В этом посте, который вдохновил меня больше всего, есть комментарий, который
NOT IN
может быть медленнее. Но это для SQL server, и иногда элегантность важнее :) - я тоже думаю, что все зависит от хорошего индексы.В любом случае, обычно это плохой дизайн, чтобы иметь таблицы без PK. Вы должны, по крайней мере, добавить идентификатор "autoinc", и если вы это сделаете, вы можете использовать этот идентификатор вместо функции
ROWID()
, которая является нестандартным расширением Sybase (некоторые другие тоже имеют его).