Как удалить повторяющиеся строки в sybase, если у вас нет уникального ключа?
Да, вы можете найти похожие вопросы много раз, но:
самые изящные решения, размещенные здесь, работают для SQL Server, но не для Sybase (в моем случае Sybase Anywhere 11). Я даже нашел некоторые вопросы, связанные с Sybase, помеченные как дубликаты для вопросов SQL Server, что не помогает.
Один пример для решений, которые мне понравились, но не сработал, - это конструкция WITH ... DELETE ...
.
Я нашел рабочие решения, используя курсоры или while-loops, но я надеюсь, что это возможно без циклов.
Я надеюсь на хороший, простой и быстрый запрос, просто удалив все, кроме одного, точного дубликата.
Здесь немного рамки для тестирования:
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;
Ответы
Ответ 1
Хорошо, теперь, когда я знаю функцию 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-сервера, а иногда и элегантность важнее:) - Я также думаю, что все зависит от хороших индексов.
В любом случае, обычно это плохая конструкция, чтобы иметь таблицы без ПК. Вы должны хотя бы добавить "autoinc" ID, и если вы это сделаете, вы можете использовать этот идентификатор вместо функции ROWID()
, которая является нестандартным расширением Sybase (некоторые из них тоже есть).
Ответ 2
Если все поля идентичны, вы можете просто сделать это:
select distinct *
into #temp_table
from table_with_duplicates
delete table_with_duplicates
insert into table_with_duplicates select * from #temp_table
Если все поля не идентичны, например, если у вас есть другой идентификатор, вам нужно будет отобразить все поля в инструкции select и записать код в id, чтобы сделать его идентичным, если это поле, которое вас не волнует.
Например:
insert #temp_table field1, field2, id select (field1, field2, 999)
from table_with_duplicates
Ответ 3
Это работает хорошо и быстро:
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
, чтобы узнать, что он делает.
Ответ 4
Пожалуйста, попробуйте следующее:
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
Ответ 5
Вот еще один интересный, который я нашел и принял:
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()
, мне было бы интересно увидеть их.