Нумерация строк в соответствии с порядком, заданном значениями первичного ключа
Естественно, нумероваться строки должны в соответствии с некоторым порядком. Пусть этот порядок задается столбцом первичного ключа, то есть в порядке возрастания (или убывания) значений в этом единственном столбце сортировки. Для определенности предположим, что нам нужно перенумеровать модели в таблице Product, где номер модели как раз является первичным ключом. Существенным здесь является то, значения первичного ключа не содержат дубликатов и NULL-значений, в результате чего имеется принципиальная возможность установить однозначное соответствие между номером модели и номером строки в заданном порядке сортировки моделей.
Рассмотрим сначала следующий запрос:
SELECT P1.model, P2.model
FROM Product P1
JOIN Product P2 ON P1.model <= P2.model;
[[ column ]] |
---|
NULL [[ value ]] |
Здесь выполняется соединение двух идентичных таблиц по неравенству P1.model <= P2.model, в результате чего каждая модель из второй таблицы (P2.model) будет соединяться только с теми моделями из первой таблицы (P1.model), номера которых меньше или равны номеру этой модели. В результате получим, например, что модель с минимальным номером (1121) будет присутствовать во втором столбце результирующего набора только один раз, так как она меньше или равна только самой себе. На другом конце будет находиться модель с максимальным номером, так как любая модель будет меньше или равна ей. Следовательно, модель с максимальным номером будет сочетаться с каждой моделью, и число таких сочетаний будет равно общему числу моделей в таблице Product.
Из сказанного выше ясно, что это количество раз, которое каждая из моделей встречается во втором столбце результирующего набора, как раз и будет порядковым номером модели при сортировке моделей по возрастанию.
Таким образом, чтобы решить нашу задачу нумерации, достаточно пересчитать модели в правом столбце, что нетрудно сделать при помощи группировки и использования агрегатной функции COUNT:
Решение 8.2.1
SELECT COUNT(*) no, P2.model
FROM Product P1
JOIN Product P2 ON P1.model <= P2.model
GROUP BY P2.model;
[[ column ]] |
---|
NULL [[ value ]] |
Не будем экономить место и представим результат выполнения этого запроса:
no | model |
---|---|
1 | 1121 |
2 | 1232 |
3 | 1233 |
4 | 1260 |
5 | 1276 |
6 | 1288 |
7 | 1298 |
8 | 1321 |
9 | 1401 |
10 | 1408 |
11 | 1433 |
12 | 1434 |
13 | 1750 |
14 | 1752 |
15 | 2112 |
16 | 2113 |
Для нумерации в обратном порядке достаточно поменять знак неравенства на противоположный.
Если ваша СУБД поддерживает ранжирующие функции, то пронумеровать строки можно совсем просто:
SELECT ROW_NUMBER() OVER(ORDER BY model) no, model
FROM Product;
[[ column ]] |
---|
NULL [[ value ]] |
Несколько усложним задачу, и попытаемся пронумеровать модели каждого производителя отдельно. Воспользуемся предыдущим решением, и внесем в него следующие изменения:
- Добавим в условие соединения равенство производителей, чтобы выделить модели каждого производителя в отдельную группу.
SELECT COUNT(*) no, P2.model
FROM Product P1
JOIN Product P2 ON P1.maker =P2.maker
AND P1.model <= P2.model
GROUP BY P2.model
ORDER BY P2.model;
[[ column ]] |
---|
NULL [[ value ]] |
В принципе, это и все. Правда, результат не отличается наглядностью.
- Добавим в вывод производителя, при этом все равно из какой таблицы мы его возьмем в силу равенства. Однако тогда необходимо добавить производителя в столбцы группировки (MySQL не в счет):
SELECT COUNT(*) no, P1.maker, P2.model
FROM Product P1
JOIN Product P2 ON P1.maker =P2.maker
AND P1.model <= P2.model
GROUP BY P1.maker, P2.model
ORDER BY P2.model;
[[ column ]] |
---|
NULL [[ value ]] |
- Ну и, наконец, добавим сортировку для наглядности результата. Столбец maker должен быть первым столбцом сортировки, чтобы каждая группа выводилась отдельно.
SELECT COUNT(*) no, P1.maker, P2.model
FROM Product P1
JOIN Product P2 ON P1.maker =P2.maker
AND P1.model <= P2.model
GROUP BY P1.maker, P2.model
ORDER BY P1.maker, P2.model;
[[ column ]] |
---|
NULL [[ value ]] |
no | maker | model |
---|---|---|
1 | A | 1232 |
2 | A | 1233 |
3 | A | 1276 |
4 | A | 1298 |
5 | A | 1401 |
6 | A | 1408 |
7 | A | 1752 |
1 | B | 1121 |
2 | B | 1750 |
1 | C | 1321 |
1 | D | 1288 |
2 | D | 1433 |
1 | E | 1260 |
2 | E | 1434 |
3 | E | 2112 |
4 | E | 2113 |
Я надеюсь, что выполнить раздельную нумерацию моделей по типам продукции вам теперь не составит труда. Сделайте это в качестве самостоятельного задания.
Ранжирующие функции, естественно, упрощают запрос:
SELECT ROW_NUMBER() OVER(PARTITION BY maker ORDER BY model) no,
maker, model
FROM Product
ORDER BY maker, model;
[[ column ]] |
---|
NULL [[ value ]] |