2013-02-20 9 views
0

У меня есть таблица, в которой хранятся данные, где случайные данные были сохранены несколько раз из-за чувствительности к регистру имени пользователя на стороне сервера. Поле имени пользователя должно рассматриваться как нечувствительное к регистру. Важные столбцы и данные для таблицы можно найти ниже.Выбор строк на основе двух столбцов в SQL Server

Мои требования теперь состоят в том, чтобы удалить все, кроме последних сохраненных данных. Я пишу скрипт sql для этого и начинаю, идентифицируя все строки, которые являются дубликатами. Этот выбор возвращает таблицу, как показано ниже.

Для каждой строки последнее сохранение - LASTUPDATEDDATE, если оно существует, иначе CREATEDDATE. Для этого примера, самый последний за исключением «имя пользователя» будет Row 3.

 
ID CREATEDDATE LASTUPDATEDDATE USERNAME       
-- ----------- --------------- -------- 
1 11-NOV-11     USERNAME         
2 01-NOV-11 02-NOV-11  username        
3 8-JAN-12     USERname  

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


SELECT 
    id, createddate, lastupdateddate, username 
FROM 
    table 
WHERE 
    LOWER(username) 
IN 
    (
    SELECT 
     LOWER(username) 
    FROM 
     table 
    GROUP BY 
     LOWER(username) 
    HAVING 
     COUNT(*) > 1 
) 
ORDER BY 
    LOWER(username) 

Мой вопрос сейчас is: Как выбрать все, кроме строки 3? Я искал Stack Overflow для хорошего совпадения с этим вопросом, но не нашел подходящего соответствия. Я знаю, что я, вероятно, должен сделать что-то вроде такого, но не могу его обмануть. Было бы очень благодарно за толкание в правильном направлении.

Мы используем SQL Server, возможно, совершенно новую версию.

ответ

1

Для удаления дубликатов, вы можете использовать:

with todelete as (
    select t.*, 
      row_number() over (partition by lower(username) order by createddate desc) as seqnum 
    from table 
    ) 
delete from t 
where seqnum > 1 

Это присваивает порядковый номер каждой строки, начиная с 1 для самых последних. Затем он удаляет все, кроме самого последнего.

Для двух дат, вы можете использовать:

with todelete as (
    select t.*, 
      row_number() over (partition by lower(username) order by thedate desc) as seqnum 
    from (select t.*, 
        (case when createddate >= coalesdce(updateddate, createddate) 
         then createddate 
         else updateddate 
        end) as thedate 
      from table 
     ) t 
    ) 
delete from t 
where seqnum > 1 
1

Пару вещей отметить, - нет никаких оснований для использования LOWER в запросе. A = a в SQL Server.

Кроме того, чтобы получить правильную дату, вы можете использовать COALESCE, чтобы определить, существует ли LastUpdatedDate, и если да, сортируйте по нему, иначе сортируйте по CreateDate.

Полагая, что вместе, это должно работать:

DELETE T 
FROM YourTable T 
    JOIN (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY username 
        ORDER BY COALESCE(lastupdateddate, createddate) DESC) as RN 
    FROM YourTable 
    ) T2 ON T.Id = T2.Id 
WHERE T2.RN > 1 

Вот пример скрипку: http://www.sqlfiddle.com/#!3/51f7c/1

Как @Gordon правильно говорит, вы можете также использовать CTE в зависимости от версии SQL Server вы (2005+):

WITH CTE AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY username 
           ORDER BY COALESCE(lastupdateddate, createddate) DESC) as RN 
    FROM YourTable 
    ) 
DELETE FROM CTE WHERE RN > 1 
Смежные вопросы