Материал: Using_MySql,_MS_SQL_Server_and_Oracle(1)

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Пример 8: поиск множества минимальных и максимальных значений

В строке 5 запроса 2.1.8.b для Oracle мы используем специальную аналитическую функцию ROW_NUMBER, позволяющую присвоить строке номер на основе выражения. В нашем случае выражение имеет упрощённый вариант: не указан принцип разбиения строк на группы и перезапуска нумерации — мы лишь просим СУБД пронумеровать строки в порядке их следования в упорядоченной выборке.

Oracle не позволяет в одном и том же запросе как пронумеровать строки, так и наложить условие на выборку на основе этой нумерации, потому мы вынуждены использовать подзапрос (строки 3-6). Альтернативой подзапросу может быть т.н. CTE (Common Table Expression, общее табличное выражение), но о CTE мы поговорим позже{73}.

Частым вопросом относительно решения для Oracle является применимость здесь не функции ROW_NUMBER, а «псевдополя» ROWNUM. Его применять нельзя, т.к. нумерация с его использованием происходит до срабатывания ORDER BY.

Исследование 2.1.8.EXP.A. Продемонстрируем результат неверного использования «псевдополя» ROWNUM вместо функции ROW_NUMBER.

Oracle

Исследование 2.1.8.EXP.A, пример неверного запроса

1SELECT "b_name",

2"b_quantity"

 

3

 

FROM

(SELECT "b_name",

 

 

 

 

4

 

 

 

 

"b_quantity",

 

 

 

 

5

 

 

 

 

ROWNUM AS "rn"

 

 

 

 

6

 

 

FROM

"books"

 

 

 

 

7

 

 

ORDER

BY "b_quantity" DESC)

 

 

 

8

 

WHERE

"rn" = 1

 

 

 

 

 

 

 

 

 

Результатом выполнения этого запроса является:

 

 

 

 

 

 

 

 

 

 

 

 

b_name

 

b_quantity

 

 

 

 

 

Евгений Онегин

2

 

 

 

 

 

 

 

 

 

Такой результат получается потому, что подзапрос (строки 3-7) возвращает

следующие данные:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

b_name

 

b_quantity

rn

Курс теоретической физики

 

12

6

 

Искусство программирования

 

7

7

 

Основание и империя

 

5

3

 

Сказка о рыбаке и рыбке

 

3

2

 

Язык программирования С++

 

3

5

 

 

Евгений Онегин

 

 

 

2

1

 

Психология программирования

 

1

4

 

Легко заметить, что нумерация строк произошла до упорядочивания, и первый номер был присвоен книге «Евгений Онегин». При этом вариант с функцией ROW_NUMBER работает корректно.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 45/545

Пример 8: поиск множества минимальных и максимальных значений

Решение 2.1.8.c{43}.

Если нам нужно показать все книги, представленные равным максимальным количеством экземпляров, нужно выяснить это максимальное количество и использовать полученное значение как условие выборки.

MySQL Решение 2.1.8.c

1-- Вариант 1: использование MAX

2SELECT `b_name`,

3`b_quantity`

4

 

FROM

`books`

 

5

 

WHERE

`b_quantity` = (SELECT

MAX(`b_quantity`)

6

 

 

FROM

`books`)

MS SQL Решение 2.1.8.c

1-- Вариант 1: использование MAX

2SELECT [b_name],

3[b_quantity]

4

 

FROM

[books]

 

5

 

WHERE

[b_quantity] = (SELECT

MAX([b_quantity])

6

 

 

FROM

[books])

 

 

 

 

 

1-- Вариант 2: использование RANK

2SELECT [b_name],

3[b_quantity]

4

 

FROM

(SELECT

[b_name],

5

 

 

 

[b_quantity],

6

 

 

 

RANK() OVER (ORDER BY [b_quantity] DESC) AS [rn]

7

 

 

FROM

[books]) AS [temporary_data]

8

 

WHERE

[rn] = 1

 

Oracle Решение 2.1.8.c

1-- Вариант 1: использование MAX

2SELECT "b_name",

3"b_quantity"

4

 

FROM

"books"

 

5

 

WHERE

"b_quantity" = (SELECT

MAX("b_quantity")

6

 

 

FROM

"books")

 

 

 

 

 

1-- Вариант 2: использование RANK

2SELECT "b_name",

3"b_quantity"

4

 

FROM (SELECT

"b_name",

5

 

 

"b_quantity",

6

 

 

RANK() OVER (ORDER BY "b_quantity" DESC) AS "rn"

7

 

FROM

"books")

8WHERE "rn" = 1

Вслучае с MySQL доступен только один вариант решения2: подзапросом (строки 5-6) выяснить максимальное количество экземпляров книг и использовать полученное число как условие выборки. Этот же вариант решения прекрасно рабо-

тает в MS SQL Server и Oracle.

MS SQL Server и Oracle поддерживают т.н. «оконные (ранжирующие) функции», позволяющие реализовать второй вариант решения. Обратите внимание на строку 6 этого варианта: MS SQL Server требует явного именования подзапроса, являющегося источником данных, а Oracle не требует (именование подзапроса допустимо, но в данном случае не является обязательным).

2 На самом деле, в MySQL можно эмулировать ранжирующие (оконные) функции. Примеры такой эмуляции представлены в исследовании 2.1.8.EXP.D{48} и решениях 2.2.7.d{119}, 2.2.9.d{139}.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 46/545

Пример 8: поиск множества минимальных и максимальных значений

Функция RANK позволяет ранжировать строки выборки (т.е. расставить их на 1-е, 2-е, 3-е, 4-е и так далее места) по указанному условию (в нашем случае — по убыванию количества экземпляров книг). Книги с одинаковыми количествами экземпляров будут занимать одинаковые места, а на первом месте будут книги с максимальным количеством экземпляров. Остаётся только показать эти книги, занявшие первое место.

Для наглядности рассмотрим, что возвращает подзапрос, представленный строками 4-7 второго варианта решения для MS SQL Server и Oracle:

MS SQL Решение 2.1.8.c (фрагмент запроса)

1SELECT [b_name],

2[b_quantity],

3RANK() OVER (ORDER BY [b_quantity] DESC) AS [rn]

4 FROM [books]

Oracle Решение 2.1.8.c (фрагмент запроса)

1SELECT "b_name",

2"b_quantity",

3RANK() OVER (ORDER BY "b_quantity" DESC) AS "rn"

4FROM "books"

b_name

b_quantity

rn

Курс теоретической физики

12

1

Искусство программирования

7

2

Основание и империя

5

3

Сказка о рыбаке и рыбке

3

4

Язык программирования С++

3

4

Евгений Онегин

2

6

Психология программирования

1

7

Исследование 2.1.8.EXP.B. Что работает быстрее — MAX или RANK? Используем ранее созданную и наполненную данными (десять миллионов записей) таблицу test_counts{21} и проверим.

Медианные значения времени после ста выполнений запросов таковы:

 

MS QL Server

Oracle

MAX

0.009

0.341

RANK

6.940

0.818

Вариант с MAX в данном случае работает быстрее. Однако в исследовании 2.2.7.EXP.A{119} будет показана обратная ситуация, когда вариант с ранжированием окажется значительно быстрее варианта с функцией MAX. Таким образом, вновь и вновь подтверждается идея о том, что исследование производительности стоит выполнять в конкретной ситуации на конкретном наборе данных.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 47/545

Пример 8: поиск множества минимальных и максимальных значений

Решение 2.1.8.d{43}.

Чтобы найти «абсолютного рекордсмена» по количеству экземпляров, мы используем функцию работы с множествами ALL, которая позволяет сравнить некоторое значение с каждым элементом множества.

MySQL Решение 2.1.8.d

1-- Вариант 1: использование ALL и подзапроса

2SELECT `b_name`,

3`b_quantity`

4

 

FROM

`books` AS `ext`

 

 

5

 

WHERE

`b_quantity` > ALL (SELECT

`b_quantity`

 

6

 

 

FROM

`books` AS `int`

7

 

 

WHERE

`ext`.`b_id`

!= `int`.`b_id`)

 

 

 

 

 

 

MS SQL Решение 2.1.8.d

1-- Вариант 1: использование ALL и подзапроса

2SELECT [b_name],

3[b_quantity]

4

 

FROM

[books] AS [ext]

 

 

5

 

WHERE

[b_quantity] > ALL (SELECT

[b_quantity]

 

6

 

 

FROM

[books] AS [int]

7

 

 

WHERE

[ext].[b_id]

!= [int].[b_id])

1-- Вариант 2: использование общего табличного выражения и RANK

2WITH [ranked]

3AS (SELECT [b_name],

4

 

 

[b_quantity],

5

 

 

RANK()

6

 

 

OVER (

7

 

 

ORDER BY [b_quantity] DESC) AS [rank]

8

 

FROM

[books]),

9[counted]

10AS (SELECT [rank],

11

 

 

COUNT(*)

AS [competitors]

12

 

FROM

[ranked]

 

13GROUP BY [rank])

14SELECT [b_name],

15[b_quantity]

16 FROM [ranked]

17JOIN [counted]

18ON [ranked].[rank] = [counted].[rank]

19WHERE [counted].[rank] = 1

20AND [counted].[competitors] = 1

Oracle Решение 2.1.8.d

1-- Вариант 1: использование ALL и подзапроса

2SELECT "b_name",

3"b_quantity"

4

 

FROM

"books" "ext"

 

 

5

 

WHERE

"b_quantity" > ALL (SELECT

"b_quantity"

 

 

 

 

 

 

6

 

 

FROM

"books" "int"

7

 

 

WHERE

"ext"."b_id"

!= "int"."b_id")

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 48/545

Пример 8: поиск множества минимальных и максимальных значений

Oracle Решение 2.1.8.d (продолжение)

1-- Вариант 2: использование общего табличного выражения и RANK

2WITH "ranked"

3AS (SELECT "b_name",

4

 

 

"b_quantity",

5

 

 

RANK()

6

 

 

OVER (

7

 

 

ORDER BY "b_quantity" DESC) AS "rank"

8

 

FROM

"books"),

 

 

 

 

9"counted"

10AS (SELECT "rank",

11

 

 

COUNT(*)

AS "competitors"

12

 

FROM

"ranked"

 

13GROUP BY "rank")

14SELECT "b_name",

15"b_quantity"

16 FROM "ranked"

17JOIN "counted"

18ON "ranked"."rank" = "counted"."rank"

19WHERE "counted"."rank" = 1

20AND "counted"."competitors" = 1

Первые варианты решения для всех трёх СУБД идентичны (только в Oracle здесь для именования таблицы между исходным именем и псевдонимом не должно быть ключевого слова AS). Теперь поясним, как это работает.

Одна и та же таблица books фигурирует в запросе 2.1.8.d дважды — под именем ext (для внешней части запроса) и int (для внутренней части запроса). Это нужно затем, чтобы СУБД могла применить условие выборки, представленное в строке 7: для каждой строки таблицы ext выбрать значение поля b_quantity из всех строк таблицы int кроме той строки, которая сейчас рассматривается в таблице ext.

Это фундаментальный принцип построения т.н. коррелирующих запросов, потому покажем логику работы СУБД графически. Итак, у нас есть семь книг с идентификаторами от 1 до 7:

Строка из таблицы ext

Какие строки анализируются в таблице int

1

2, 3, 4, 5, 6, 7 {т.е. все, кроме 1-й}

2

1, 3, 4, 5, 6, 7 {т.е. все, кроме 2-й}

3

1, 2, 4, 5, 6, 7 {т.е. все, кроме 3-й}

4

1, 2, 3, 5, 6, 7 {т.е. все, кроме 4-й}

5

1, 2, 3, 4, 6, 7 {т.е. все, кроме 5-й}

6

1, 2, 3, 4, 5, 7 {т.е. все, кроме 6-й}

7

1, 2, 3, 4, 5, 6 {т.е. все, кроме 7-й}

Выбрав соответствующие значения поля b_quantity, СУБД проверяет, чтобы значение, выбранное из таблицы ext было больше каждого из значений, выбранных из таблицы int:

Значение ext.b_quantity

Набор значений int.b_quantity

 

2

3, 5, 1, 3, 12, 7

 

3

2, 5, 1, 3, 12, 7

 

5

2, 3, 1, 3, 12, 7

 

1

2, 3, 5, 3, 12, 7

 

3

2, 3, 5, 1, 12, 7

 

12

2, 3, 5, 1, 3,

7

 

7

2, 3, 5, 1, 3,

12

 

 

 

 

 

Работа с MySQL, MS SQL Server и Oracle в примерах

© EPAM Systems, RD Dep, 2016–2018 Стр: 49/545

Источник: https://studfile.net/preview/16418462/