Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SQL (от Сани Сафронова).doc
Скачиваний:
71
Добавлен:
03.06.2015
Размер:
969.22 Кб
Скачать

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]