2009-07-23 4 views
2

Много лет назад во время телефонного интервью меня попросили удалить повторяющиеся строки в базе данных. Дав несколько решений, которые делают работу, я в конце концов сказал, что ограничения:Удаление повторяющихся строк в базе данных без использования rowid или создание временной таблицы

  • Предположим, таблица имеет столбец один VARCHAR
  • Невозможно использовать ROWID
  • нельзя использовать временные таблицы

Интервьюер отказавшихся чтобы дать мне ответ. С тех пор я был в тупике.

После того, как я спросил несколько коллег за эти годы, я убежден, что решения нет. Я ошибаюсь?!

+4

Ugh. Доверьтесь мне. Вы все равно не хотите эту работу. Вопрос, по которому они заставляют вас завязывать руки за спиной, как правило, предназначен для того, чтобы показать, насколько умный собеседник не тестирует кандидата. – JohnFx

+0

Спасибо, JohnFx, за поддержку ... делает меня счастливее, я не преследовал эту работу. –

+1

Последнее, что вам нужно, это босс, который не хочет решения, он хочет ИХ решение. Своя ошибка менеджера новичков и очень нарциссизм, чтобы попытаться нанять клоны себя. – JohnFx

ответ

0

Я бы поместил в столбец VARCHAR уникальный номер фиксированного размера для дублированных строк, затем проанализировал число и удалил все, кроме минимальной строки. Возможно, это и есть его ограничение VARCHAR. Но это воняет, потому что предполагает, что ваш уникальный номер подойдет. Хромой вопрос. В любом случае вы не хотели бы работать там. ;-)

+0

$ chars = array ('L', 'O'); while (1 = 1) {echo $ chars [0]; echo $ chars [1];} echo $ chars [0]; –

1

Это совершенно измученный способ сделать это, но, учитывая требования assanine, здесь реализуемое решение при условии, SQL 2005 или более поздней версии:

DELETE from MyTable 
    WHERE ROW_NUMBER() over(PARTITION BY [MyField] order by MyField)>1 
+0

Интересно - звучит как row_number() очень похоже на rowid –

+0

@vh row_number() больше похож на ROWNUM Oracle, чем ROWID от Oracle, но гораздо более гибкий. Oracle также имеет ROW_NUMBER(). Это часть аналитических функций. О, и это не будет работать в SQL Server 2005/2008, потому что ROW_NUMBER() не разрешено в предложении where. –

+0

Ack! Ты прав. Я клянусь, что это работало вчера, когда я тестировал его, но, увы, он не работает этим утром. Извините за ложную надежду. Кроме того, я просто заметил ограничение noRowID, так что это, вероятно, нарушает дух терминов. Я говорю, что вы должны просто встретить вопрос интервьюера о том, как они будут писать запрос, чтобы сделать это без клавиатуры или мыши. Это примерно так же бессмысленно. – JohnFx

2

И если вы сделали есть ответ, вдруг неожиданно представится новое ограничение? Поскольку вы упоминаете ROWID, я предполагаю, что вы использовали Oracle. Решения для SQL Server.

Вдохновленный SQLServerCentral.com http://www.sqlservercentral.com/scripts/T-SQL/62866/

while(1=1) begin 
    delete top (1) 
    from MyTable 
    where VarcharColumn in 
    (select VarcharColumn 
    from MyTable 
    group by VarcharColumn 
    having count(*) > 1) 

    if @@rowcount = 0 
     exit 
end 

Удаляет одну строку за один раз. Когда вторая до последней строки набора дубликатов исчезает, оставшаяся строка не будет в подзапросе при следующем проходе через цикл. (BIG Yuck!)

Также см. http://www.sqlservercentral.com/articles/T-SQL/63578/ для вдохновения. Там RBarry Young предлагает способ, который может быть изменен для хранения дедуплицированных данных в одной таблице, удалить все исходные строки, а затем преобразовать сохраненные дедуплицированные данные обратно в правильный формат. У него было три столбца, поэтому не совсем аналогично тому, что вы делаете.

И тогда он может быть выполнен с помощью курсора. Не уверен и не успел посмотреть. Но создайте курсор, чтобы выбрать все из таблицы в порядке, а затем переменную, чтобы отслеживать, как выглядела последняя строка. Если текущая строка такая же, удалите, иначе установите переменную в текущую строку.

+0

Вы правы ... большой yuck для этого цикла. Вы также правы, что появилось новое ограничение, когда я предложил использовать PL/SQL-процедуру. Он хотел, чтобы это было сделано только с помощью инструкции DELETE. –

+0

Эй, это я! И FYI, техника, которую я использовал в моей статье *, будет * работать и с одним столбцом VARCHAR, если она еще не выгружена в любом месте. О, и это без каких-либо циклов или курсоров (что делает его легким) и * также * совместимым с SQL 2000, поэтому никакой функции Row_Number() не существует. И да, это чертовски жестко, но это можно сделать. – RBarryYoung

0

Предположим, что вы выполняете инструкцию DELETE для механизма SQL. как вы удалите две строки из таблицы, которые точно идентичны? Вам нужно что-то отличить друг от друга! Вы на самом деле не может удалить полностью повторяющиеся строки (все столбцы равны) при следующих ограничениях (как это предусмотрено для вас)

  1. без использования ROWID или ROWNUM
  2. Нет Временная таблица
  3. Нет процедурный код

Это может быть сделано, даже если одно из условий ослаблено.Вот решения с использованием по меньшей мере одного из трех условий

Предположим, таблица определена, как показано ниже

Создать таблицу t1 (
col1 vacrchar2 (100),
col2 число (5),
col3 номер (2)
); Идентификация

повторяющиеся строки:

Выберите col1, col2, col3
от t1
группы по col1, col2, col3
, имеющий счетчик (*)> 1

повторяющиеся строки может также можно идентифицировать, используя это: выберите c1, c2, c3, row_number() over (разделение на (c1, c2, c3) по c1, c2, c3) rn
от t1

ПРИМЕЧАНИЕ. Аналитическая функция row_number() не может использоваться в операторе DELETE, как предложено JohnFx, по крайней мере, в Oracle 10g.

  • Решение с использованием ROWID

Удалить из t1, где row_id>
(выбрать мин (t1_inner.row_id) от t1 t1_innner
, где t1_inner.c1 = t1.c1 и t1_inner.c2 = t1.c2 и t1_inner.c3 = t1.c3))

  • решения с использованием временной таблицы

создать таблицу t1_dups как (
// написать запрос здесь, чтобы найти повторяющиеся строки в Liste выше //
)

удалить из t1
где t1.c1, t1.c2, t1.c3 в (выбрать * из t1.dups)
вставки в t1 (
выберите c1, c2, c3 от t1_dups)

  • Решение с использованием процедурного кода

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

0
create table temp as 
select c1,c2 
from table 
group by c1,c2 
having(count(*)>1 or count(*)=1); 

Теперь опустите базовый стол. Переименуйте таблицу temp в базовую таблицу.

Смежные вопросы