Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Новости сайта "Упражнения по SQL" (http://www.sql-ex.ru) 211


Новости сайта "Упражнения по SQL (http://www.sql-ex.ru)" Выпуск 211 (11 октября 2008 г.)

Новым посетителям сайта

SQL Exercises Сайт посвящен изучению языка, с помощью которого осуществляется взаимодействие с реляционными (и не только) СУБД. Суть обучения состоит в выполнении заданий на написание запросов к учебным базам данных; при этом система контролирует правильность выполнения заданий. В настоящее время реализованы все операторы подъязыка манипуляции данными (DML), которые включают в себя оператор извлечения данных SELECT, а также операторы модификации данных - INSERT, DELETE и UPDATE.

Мы надеемся, что справочного материала сайта окажется достаточно для самостоятельного обучения. Кроме того, свои решения вы можете обсудить на форуме сайта. Опытных же специалистов приглашаем проверить (продемонстрировать) свое мастерство и принять участие в соревновании, обеспечиваемом рейтинговой системой учета времени выполнения заданий. Фактически, рейтинг ведется на втором этапе тестирования, который начинается сейчас после решения 51-й задачи первого этапа. При подсчете рейтинга каждого участника отбрасывается один самый худший показатель среди всех решенных им упражнений.

Демонстрация плана выполнения запроса и сравнительная оценка эффективности решений поможет вам освоить принципы оптимизации запросов, которые пригодятся на третьем рейтинговом этапе, который начинается после 138 задачи.

Имеется возможность получить сертификат по SQL DML при выполнении определенного количества заданий.


Новости сайта

§ Продолжается народное голосование в рамках Премии Рунета. Поддержите сайт своими голосами! Голосовать можно раз в сутки одновременно за <=50 проектов.

§ Добавил проверочные данные в ответ на сообщение о прохождении неверных решений, сделанное Bless на форуме 66 задачи. Принял его же версию формулировки этой задачи.

§ Запущен новый проект - интерактивный учебник по SQL по материалам книги "SQL. Задачи и решения".
Планирую поддерживать его в актуальном состоянии, т.е. дополнять новыми материалами, относящимися к изменениям, происходящим как в стандарте языка, так и в его реализациях.
Заинтересованных лиц прошу потестировать функционирование сайта и сообщать о замеченных недостатках. Просьба писать о них с сайта проекта с тем, чтобы проверить заодно работу почты и чтобы ваши сообщения получили все причастные к этому проекту лица.

§ С лучшим результатом на 138-ой задаче (не считая автора) закончил второй этап @Nikotin (задач 138, время 4.885)
Одна задача до третьего этапа осталась orange (137, 13.978)

§ Изменения среди лидеров (решенные за неделю задачи третьего этапа):
1. pvv (147, 149)
3. Arcan (149)
37. @Nikotin (139)

§ Новые лица в ТОР 100 и вернувшиеся туда:
44. orange (137, 13.978)

§ Продвинулись в рейтинге:
52. D2NX (136, 506.137)
62. Oxana (133, 56.964)
80. glassman (128, 41.706)

§ Продвижение ближайших претендентов на попадание в ТОР 100:
103. TimonSP (125, 1.996)
117. lambda (122, 19.015)
122. Pavel_yu (123, 129.990)
146. MeVit (118, 26.153)

§ На этой неделе сертифицированы:
@Nikotin (B08037927) [AR] - г.Санкт-Петербург, Россия
Oxana (B08022525) [AR] - г.Москва, Россия
VOVA_KHILKO (B08032828) [AR] - г.Волгоград, Россия

§ Число подписчиков - 3843

Число участников рейтинга - 18415

Число участников второго этапа - 1567

Число участников третьего этапа - 40

Сертифицировано на сайте - 327

Лучшие результаты (ТОР 20)

No Person Number of
Sel_ex
Last_Sel Number of
DML_ex
Scores Days Days_2 Days_3 S_3 LastSolved LastVisit
1 >Печатнов В.В. (pvv) 149 147 21 363 357 30.866 17.490 36 10 Oct 2008 10 Oct 2008
2 Селезнёв А.С. (Артём С.) 148 147 21 360 322 38.520 29.234 36 25 Sep 2008 10 Oct 2008
3 Креславский О.М. (Arcan) 149 149 21 363 617 48.147 36.296 36 08 Oct 2008 10 Oct 2008
4 Карасёва Н.В. (vlksm) 149 149 21 363 866 77.241 49.173 36 18 Sep 2008 10 Oct 2008
5 Сальников С.А. ($erges) 148 54 21 361 196 2.854 3.359 34 09 Sep 2008 10 Oct 2008
6 Держальцев В.А. (MadVet) 140 15 21 344 1257 60.815 28.482 28 24 Sep 2008 06 Oct 2008
7 Любченко В.А. (IAS56) 139 146 21 343 615 403.439 373.617 28 11 May 2008 16 Aug 2008
8 Голубин Р.С. (Roman S. Golubin) 143 54 21 346 1122 93.054 58.822 25 13 Sep 2008 09 Oct 2008
9 Nikolaenko A.V. (Shadow77) 143 54 21 346 410 51.575 14.010 23 26 Sep 2008 08 Oct 2008
10 Мурашкин И.В. (lepton) 142 148 21 345 921 43.485 33.011 23 29 Sep 2008 02 Oct 2008
11 Солдатенков Ю.С. (SolYUtor) 141 146 21 342 819 22.703 6.102 20 14 Aug 2008 24 Sep 2008
12 Белогурова К. (Katy_Ekb) 135 143 21 331 361 10.714 4.673 18 20 May 2008 05 Sep 2008
13 Егоров А.Б. (ABEgorov) 140 144 21 340 180 12.917 8.815 18 03 Aug 2008 12 Aug 2008
14 Войнов П.Е. (pаparome) 142 88 21 341 1125 3.132 .213 17 22 Sep 2008 07 Oct 2008
15 iglbeat (iglbeat) 140 145 21 339 360 34.601 15.225 17 08 Aug 2008 25 Aug 2008
16 Северюхин Ю.А. (Venser) 131 142 21 322 335 4.925 .655 14 01 Feb 2008 04 Feb 2008
17 Дроздков А.Н. (anddros) 142 141 21 341 121 4.095 .780 14 25 Sep 2008 10 Oct 2008
18 Борисенков Д.В. (xuser) 137 74 21 331 247 3.280 .926 14 04 Oct 2008 10 Oct 2008
19 Мишин С.А. (CepbIu) 137 142 21 331 69 9.772 2.148 14 26 Jun 2008 10 Sep 2008
20 Тарасов Д.Б. (Gavrila) 138 142 21 333 1069 24.230 2.501 14 30 Jul 2008 10 Oct 2008

Лучшие результаты за неделю

No surname n_sel sel_all sel_scores dml_scores scores rating last_visit
1 >Ябжанов В. (yabsql) 48 48 90 5 95 1701 10 Oct 2008
2 Ведмецкий К.В. (kvedmetskij) 32 32 62 26 88 1950 10 Oct 2008
3 >Konurbayev V. (Sirius-V) 39 39 75 9 84 2102 10 Oct 2008
4 >Суслов Д. (suncloud) 38 38 74 3 77 2424 10 Oct 2008
5 >Kling S. (DBA Lamer) 38 38 74 0 74 2608 10 Oct 2008
6 >zubovich (zn) 34 34 65 0 65 3152 10 Oct 2008
7 Тхир В.Е. (Woldemar) 22 33 46 8 54 3098 09 Oct 2008
8 Сидельников (genna) 15 28 36 17 53 2965 10 Oct 2008
9 Сколоздра Ю. (SYR) 28 28 52 0 52 4254 08 Oct 2008
10 >Bychkov I. (AgDanila1321) 28 28 52 0 52 4282 10 Oct 2008
11 Жуков М.М. (panamec) 21 35 50 0 50 2875 07 Oct 2008
12 Вакарин К. (Скрипач) 26 27 48 0 48 4687 09 Oct 2008
13 >Вартазарян (GANZA) 21 47 45 0 45 1855 10 Oct 2008
14 Двуреченский П. (Dvurechensky) 18 29 44 0 44 3880 09 Oct 2008
15 First (Denis_2710) 24 24 43 1 44 5120 07 Oct 2008
16 >Медведев В.В. (MeVit) 15 118 39 0 39 146 10 Oct 2008
17 Емельянов К.С. (gipsy) 2 67 5 34 39 590 10 Oct 2008
18 >Беленко В.В. (Riffi) 16 26 39 0 39 4541 10 Oct 2008
19 Супрун Д.В. (TimonSP) 15 125 37 0 37 103 10 Oct 2008
20 Litvinov A. (Litvin) 18 23 37 0 37 5109 07 Oct 2008
21 Варенник С.В. (kittyspark) 18 18 26 11 37 5923 10 Oct 2008
22 ps (jff) 15 15 19 17 36 6033 07 Oct 2008
23 >Бобылев А. (KittenLS) 14 28 35 0 35 4300 10 Oct 2008
24 Чойбеков Б.Э. (Bolat) 9 46 17 17 34 1423 10 Oct 2008
25 рослякова Т. (nosebien) 10 34 23 11 34 2578 10 Oct 2008
26 >Лобанов Е.М. (E_u_g_e_n_E) 22 22 34 0 34 6243 10 Oct 2008
27 Комарова Т.С. (lambda) 13 122 33 0 33 117 09 Oct 2008
28 Махоткин С.К. (mserge) 20 20 33 0 33 6417 07 Oct 2008
29 >Gulina M.V. (proliv) 4 71 11 21 32 499 10 Oct 2008
30 Lentsevich Y.A. (Lentsevich Y.A.) 2 38 5 25 30 1425 10 Oct 2008
31 Monchenko S. (msv_sergey) 17 54 30 0 30 1581 09 Oct 2008
32 Кожаров А.В. (Dergard) 13 24 30 0 30 5478 09 Oct 2008
33 Ильин Е.А. (EvGeniy Lell) 12 12 13 17 30 6966 07 Oct 2008

Изучаем SQL

Требования к SQL Server по усилению поддержки: предложение OVER() и упорядоченные вычисления (продолжение, начало в вып.195-199, 202-210)

Itzik Ben-Gan и Sujata Mehta (оригинал: SQL Server Feature Enhancement Request - OVER Clause and Ordered Calculations )
Перевод: Карасева Н.В.

ii. Функции LAG и LEAD

Функции LAG и LEAD позволяют вам получать значения записей с заданным смещением (в терминах числа строк) относительно к текущей строки. Эти функции могут быть очень полезны для решения задач бизнеса, таких как расчет трендов, сравнение значений в соседних строках, а также для определения пропусков в последовательностях.

Синтаксис для этих функций:

{LAG | LEAD}(<выражение>, <смещение>, <значение_по_умолчанию>) OVER(…)

LAG возвращает <выражение> из строки, сдвинутой на число строк, равного <смещению> вверх по отношению к текущей, и если такая строка не найдена, то возвращается <значение_по_умолчанию>. LEAD работает так же, только <смещение> будет выполнено в сторону последующих строк, а не предшествующих.

Классическое применение этих функций - сопоставление значений из предшествующих или последующих строк со значениями в текущей строке. Например, (используется таблица EmpOrders), предположим, что вам нужно вычислить тренды сбыта продукции каждым работником путем сопоставления деятельности работника в текущем месяце с деятельностью за предыдущие месяцы. Без функции LAG вам пришлось бы использовать развернутые подзапросы или оператор APPLY. С функцией LAG все гораздо проще, да оптимальность такого решения будет потенциально выше за счет единственного сканирования данных. Если существует индекс (для столбцов разбиения, сортировки и окна), то возможен один проход уже упорядоченного индекса, что позволяет избежать дополнительной сортировки. Вот как выглядело бы такое решение:

SELECT empid, ordermonth, qty as qtythismonth,
  qty - LAG(qty, 1, NULL) OVER(PARTITION BY empid
                               ORDER BY ordermonth) AS qtydiff
FROM dbo.EmpOrders;

Заметим, что функции LAG и LEAD являются не стандартными, а предложенными в качестве расширения стандарта, подобного сделанному в Oracle. Выражение:

LAG(qty, 1, NULL) OVER(PARTITION BY empid ORDER BY ordermonth)

может быть выражено стандартными средствами посредством вычислений на базе OVER:

MIN(qty) OVER (PARTITION BY empid ORDER BY ordermonth
               ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING)

Однако использование LAG и LEAD в подобных случаях выглядят заметно проще и интуитивно понятней.

Такие вычисления уместны также и для транзакционных данных. Для примера, взяв таблицу Orders в базе данных Northwind, предположим, что вам нужно сравнить каждый текущий заказ работника с предыдущим заказом с тем, чтобы вычислить, сколько дней прошло между датами текущего и предыдущего заказов. Предшествование заказов работника основано на OrderDate, OrderID. OrderID используется как разрыв цепочки (tiebreaker).

Без использования функции LAG возможны несколько решений. Например, вы можете использовать подзапрос с TOP 1 и соединения:

SELECT Cur.EmployeeID,
  Cur.OrderID AS CurOrderID, Prv.OrderID AS PrvOrderID,
  Cur.OrderDate AS CurOrderDate, Prv.OrderDate AS PrvOrderDate,
  Cur.RequiredDate AS CurReqDate, Prv.RequiredDate AS PrvReqDate
FROM (SELECT EmployeeID, OrderID, OrderDate, RequiredDate,
        (SELECT TOP(1) OrderID
         FROM dbo.Orders AS O2
         WHERE O2.EmployeeID = O1.EmployeeID
           AND (O2.OrderDate < O1.OrderDate
                OR O2.OrderDate = O1.OrderDate
                   AND O2.OrderID < O1.OrderID)
         ORDER BY OrderDate DESC, OrderID DESC) AS PrvOrderID
      FROM dbo.Orders AS O1) AS Cur
  LEFT OUTER JOIN dbo.Orders AS Prv
    ON Cur.PrvOrderID = Prv.OrderID
ORDER BY Cur.EmployeeID, Cur.OrderDate, Cur.OrderID;

Этот запрос сопоставляет текущий заказ с предшествующим заказом. Конечно, теперь вы можете рассчитать разности для значений атрибутов текущей и предшествующей строки. Это решение весьма неэффективно. Вы можете несколько оптимизировать его путем использования оператора APPLY:

SELECT Cur.EmployeeID,
  Cur.OrderID AS CurOrderID, Prv.OrderID AS PrvOrderID,
  Cur.OrderDate AS CurOrderDate, Prv.OrderDate AS PrvOrderDate,
  Cur.RequiredDate AS CurReqDate, Prv.RequiredDate AS PrvReqDate
FROM dbo.Orders AS Cur
  OUTER APPLY
    (SELECT TOP(1) OrderID, OrderDate, RequiredDate
     FROM dbo.Orders AS O
     WHERE O.EmployeeID = Cur.EmployeeID
       AND (O.OrderDate < Cur.OrderDate
            OR (O.OrderDate = Cur.OrderDate
               AND O.OrderID < Cur.OrderID))
     ORDER BY OrderDate DESC, OrderID DESC) AS Prv
ORDER BY Cur.EmployeeID, Cur.OrderDate, Cur.OrderID;

При существовании индекса на EmployeeID, OrderDate, OrderID с добавлением RequiredDate для покрытия запрос будет выполнять операцию поиска (seek) в индексе для каждой внешней строки. Это решение, хоть и эффективнее предыдущего, но весьма незначительно. Если у вас N записей в таблице, и стоимость операции поиска составляет S чтений, то стоимость данного решения N + N*S. Например, для таблицы с 1.000.000 записями и 3-мя уровнями в индексе, этот запрос потребует свыше 3.000.000 чтений.

Вы можете оптимизировать решение (как и решение предыдущей задачи определения тренда) путем использования функции ROW_NUMBER, которая имеется в SQL Server 2005:

WITH OrdersRN AS
(
  SELECT EmployeeID, OrderID, OrderDate, RequiredDate,
    ROW_NUMBER() OVER(PARTITION BY EmployeeID
                      ORDER BY OrderDate, OrderID) AS rn
  FROM dbo.Orders
)
SELECT Cur.EmployeeID,
  Cur.OrderID AS CurOrderID, Prv.OrderID AS PrvOrderID,
  Cur.OrderDate AS CurOrderDate, Prv.OrderDate AS PrvOrderDate,
  Cur.RequiredDate AS CurReqDate, Prv.RequiredDate AS PrvReqDate
FROM OrdersRN AS Cur
  LEFT OUTER JOIN OrdersRN AS Prv
    ON Cur.EmployeeID = Prv.EmployeeID
    AND Cur.rn = Prv.rn + 1
ORDER BY Cur.EmployeeID, Cur.OrderDate, Cur.OrderID;

Это решение потребует 2-х операций сканирования (scan) индекса плюс, конечно, стоимость соединения. С функцией LAG потенциально существует возможность решения с единственной операцией упорядоченного сканирования индекса. Решение могло бы выглядеть так:

SELECT EmployeeID,
  OrderID AS CurOrderID,
  LAG(OrderID, 1, NULL)
    OVER(PARTITION BY EmployeeID
         ORDER BY OrderDate, OrderID) AS PrvOrderID,
  OrderDate AS CurOrderDate,
  LAG(OrderDate, 1, NULL)
    OVER(PARTITION BY EmployeeID
         ORDER BY OrderDate, OrderID) AS PrvOrderDate,
  RequiredDate AS CurReqDate,
  LAG(RequiredDate, 1, NULL)
    OVER(PARTITION BY EmployeeID
         ORDER BY OrderDate, OrderID) AS PrvReqDate
FROM Orders;

Видно, что это решение проще, интуитивно понятнее и потенциально имеет лучшую производительность.

Функцию LAG можно также использовать как средство достижения цели в других задачах. Например, в широко распространенной классической задаче, упомянутой ранее, когда вам нужно определить пропуски в последовательности.

Дана следующая таблица T1:

USE tempdb;
GO
IF OBJECT_ID('dbo.T1') IS NOT NULL
  DROP TABLE dbo.T1
GO
CREATE TABLE dbo.T1(col1 INT NOT NULL PRIMARY KEY);
INSERT INTO dbo.T1(col1) VALUES(1);
INSERT INTO dbo.T1(col1) VALUES(2);
INSERT INTO dbo.T1(col1) VALUES(3);
INSERT INTO dbo.T1(col1) VALUES(100);
INSERT INTO dbo.T1(col1) VALUES(101);
INSERT INTO dbo.T1(col1) VALUES(103);
INSERT INTO dbo.T1(col1) VALUES(104);
INSERT INTO dbo.T1(col1) VALUES(105);
INSERT INTO dbo.T1(col1) VALUES(106);

Вам нужно определить пропуски в значениях col1. Покажем, как можно использовать функцию LEAD для решения этой задачи:

WITH C AS
(
  SELECT col1 AS cur, LEAD(col1, 1, NULL) OVER(ORDER BY col1) AS nxt
  FROM dbo.T1
)

SELECT cur + 1 AS start_gap, nxt - 1 AS end_gap
FROM C
WHERE nxt - cur > 1;

Как вы можете видеть, решение просто, интуитивно понятно и может быть оптимизировано с помощью выполнения единственной операции упорядоченного сканирования индекса, определенного на col1.

Выводы: функции LAG и LEAD дают возможность решить общие проблемы, когда нужно получить значения из строки, находящейся на некотором расстоянии от текущей строки. Примеры таких задач, рассмотренных в этом разделе, включают: вычисления трендов, сопоставление значений текущей и предыдущей транзакций, определение пропусков в последовательности и, конечно, многие другие.

(Продолжение следует...)

Полезная информация

§ Онлайновый выпуск рассылки можно почитать на сайте.

§ Все статьи, публикуемые в рассылке, затем выкладываются на сайте Книги и статьи по SQL.

§ Хотите поддержать проект? Вот инструкция по применению. :-)

Контакты

По всем вопросам, связанным с функционированием сайта, проблемами при решении упражнений, идеями вы можете обращаться к Сергею И.Моисеенко msi77[@]yandex.ru. Вы также можете предложить свои задачи для публикации на сайте.

Подписка Subscribe.Ru
Новости сайта "Упражнения по SQL"

В избранное