Как удалить повторяющиеся строки в 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(), мне было бы интересно увидеть их.