
- •Оглавление Извлечение данных. Оператор select
- •Оператор select
- •Предикаты (часть I)
- •Предикаты сравнения
- •Предикат between
- •Предикат in
- •Переименование столбцов и вычисления в результирующем наборе
- •Предикаты (часть 2) Предикат like
- •Использование значения null в условиях поиска Предикат is [not] null
- •Получение итоговых значений
- •Предложение group by
- •Предложение having
- •Использование в запросе нескольких источников записей
- •Явные операции соединения
- •Традиционные операции над множествами и оператор select
- •Декартово произведение
- •Объединение
- •Пересечение и разность
- •Предикат exists
- •Использование ключевых слов some | any и all с предикатами сравнения
- •Еще о подзапросах
- •Преобразование типов
- •Оператор case
- •Функции Transact-sql для обработки даты/времени
- •Функция dateadd
- •Функция datediff
- •Функция datepart
- •Функция datename
- •Функции работы со строками в ms sql server 2005
- •Функция substring
- •Функция reverse
- •Функция replace
- •Функции ltrim и rtrim
- •Функции lower и upper
- •Функция unicode
- •Функция nchar
- •Операторы модификации данных
- •Оператор insert
- •Вставка строк в таблицу, содержащую автоинкрементируемое поле
- •Оператор update
- •Оператор delete
- •Приложение 1. Описание учебных баз данных
- •1. Компьютерная фирма
- •2. Фирма вторсырья
- •3. Корабли
- •4. Аэрофлот
- •5. Окраска
- •Приложение 2. Список упражнений (select)
- •Как объединить данные из двух столбцов в один без использования union и join?
- •1. Union all
- •2. Full join
- •3. Unpivot
- •Комментарии
- •Как добавить новый столбец в таблицу между существующими столбцами?
- •Как вывести по n строк из каждой группы?
- •"Классическое" решение
- •1. Соединение
- •2. Подзапрос в предложении select
- •Решение на основе ранжирующих функций
- •Как удалить дубликаты строк из таблицы?
- •1. Нумерация строк
- •2. Ранжирование строк внутри групп дубликатов
- •3. Удаление дубликатов из виртуальной таблицы
- •Как удалить дубликаты строк при наличии первичного ключа?
- •Id name
- •Id_pk color
- •Id name color
- •Обновление таблицы t_details
- •Id_pk name color dup min_id
- •Id_pk color
- •Id name
- •Id_pk color
1. Нумерация строк
Мы не можем сразу ранжировать строки просто потому, что их не по чем ранжировать. Дело в том, что одинаковые строки будут иметь одинаковый ранг. Поэтому сначала пронумеруем их, упорядочив по столбцу name.
SELECT *, ROW_NUMBER() OVER(ORDER BY name) num
FROM T
В результате получим
name num
John 1
John 2
Smith 3
Smith 4
Smith 5
Tom 6
2. Ранжирование строк внутри групп дубликатов
К сожалению, запрещено (MS SQL Server) использовать оконные функции внутри оконных функций. Т.е. мы не можем написать так:
SELECT name,
RANK() OVER (PARTITION BY name ORDER BY ROW_NUMBER()
OVER(ORDER BY name)) rnk
FROM T;
а потому используем подзапрос:
SELECT name, RANK() OVER (PARTITION BY name ORDER BY num) rnk
FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY name) num
FROM T
) X;
Ниже представлен результат этого запроса.
name rnk
John 1
John 2
Smith 1
Smith 2
Smith 3
Tom 1
3. Удаление дубликатов из виртуальной таблицы
Недопустимо удалять записи из запроса, т.е. мы не можем воспользоваться таким вариантом:
DELETE FROM (SELECT name, RANK() OVER (PARTITION BY name ORDER BY num) rnk
FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY name) num
FROM T
) X
WHERE rnk > 1;
т.к. в операторе DELETEдопускается использовать только базовую таблицу или представление. Поэтому мы могли бы создать представление и удалить записи уже из него. Конечно, на самом деле записи удаляются из базовой таблицы, на которой создано представление. Итак, мы можем поступить следующим образом:
CREATE VIEW Tview
AS
SELECT name, RANK() OVER (PARTITION BY name ORDER BY num) rnk
FROM(SELECT *, ROW_NUMBER() OVER(ORDER BY name) num
FROM T
) X
GO
DELETE FROM Tview
WHERE rnk > 1;
"Опять что-то создавать", - скажете вы. Не обязательно, и, чтобы доказать это, нам помогут общие табличные выражения(CTE), которые можно назвать виртуальными представлениями. CTE, хотя и не являются сохраняемыми в базе данных объектами, могут использоваться с операторами обновления. В результате все сводится к одному запросу:
WITH CTE AS
(SELECT name, RANK() OVER (PARTITION BY name ORDER BY num) rnk
FROM(SELECT *, ROW_NUMBER() OVER(ORDER BY name) num
FROM T
) X
)
DELETE FROM CTE
WHERE rnk > 1;
GO
Не забудьте только создать первичный ключ. :-)
06-10-2009
Попал по внешней ссылке на эту статью и решил себе возразить. :-)
Вот эта фраза: "Мы не можем сразу ранжировать строки просто потому, что их не по чем ранжировать."
Разумеется, это правильно, но мы можем отказаться от ранжирования (в ущерб обучению :-)), выполнив "псевдоранжирование". Дело в том, что есть возможность выполнить независимую нумерацию для каждой группы, если в предложении OVER для функции ROW_NUMBER использовать конструкцию PARTITION BY. Итак, можно вообще обойтись без функции RANK, если выполнить разбиение по name
SELECT name, ROW_NUMBER() OVER(PARTITION BY name ORDER BY name)
FROM T;
Это упростит все последующие запросы, в частности, последнее решение задачи удаления дубликатов можно переписать в виде:
WITH CTE AS (
SELECT name, ROW_NUMBER() OVER(PARTITION BY name ORDER BY name) rnk
FROM T
)
DELETE FROM CTE
WHERE rnk > 1;
Dzone.com