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

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


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

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

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

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

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

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


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

§ Выполнил компенсацию времени, связанную с простоем с 26-08-2008 на 27-08-2008.

§ Почти исправил баг форума, связанный с появлением пробельных строк в скрипте под Мозиллой/Оперой.
"Почти" означает, что по неизвестным мне причинам не все скрипты отображаются без пробельных строк. Но уже это можно исправить редактированием сообщения. Как показали эксперименты, достаточно убрать и поставить заново перевод каретки буквально в одной-двух первых строчках скрипта.

§ На форуме появились топики 777 и 778. Задач с такими номерами нет. Это - ветки для обсуждения планов развития сайта с лидерами рейтинговой системы (777) и проблем оптимизации запросов (778). Доступ к первому топику имеют те, кто перешел на третий этап, а ко второму - преодолевшие первый сертификационный порог (наличие сертификата не обязательно, но желательно :-)).

§ Еще один вариант решения задачи об объединении данных из двух столбцов в один от sql chuvak опубликован в виде комментария к статье.

§ Усилил проверку задачи 56 (сообщение Warri).

§ По просьбе $erges переписал рейтинг задач второго этапа. Раньше нельзя было посмотреть начавших решать новую задачу до тех пор, пока кто-нибудь помимо автора не решит ее.

§ К следующей рассылке планирую выставить несколько новых задач. Обновление коснется всех этапов.
Появится возможность посмотреть, как работает рейтинг задач. :-)

§ Изменения среди лидеров (решенные за неделю задачи третьего этапа):
8. Артём С. (144)
29. rage (141, 142)
31. anddros (139, 142)
33. mz (139)

§ Одна задача до 3 этапа осталась:
42. Fomichev (137, 19.301)

§ Продвинулись в рейтинге:
49. yoj (136, 12.703)
51. nebiros (136, 358.586)
53. Umrikhina (135, 4.386)
60. wasp (133, 125.051)
64. Bulldozer (132, 184.232)
65. Guy (131, 2.549)
71. shadon (130, 24.483)

§ Продвижение ближайших претендентов на попадание в ТОР 100:
115. D-m-i-t-r-y (122, 43.405)
144. Mikha (117, 9.431)
163. eli0n (113, 70.642)
176. Oxana (110, 33.302)

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

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

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

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

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

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

No Person Number of
Sel_ex
Last_Sel Number of
DML_ex
Scores Days Days_2 Days_3 S_3 LastSolved LastVisit
1 Сальников С.А. ($erges) 147 147 21 357 118 2.842 3.359 31 23 Jun 2008 29 Aug 2008
2 Креславский О.М. (Arcan) 147 147 21 357 517 37.671 27.498 31 30 Jun 2008 29 Aug 2008
3 Карасёва Н.В. (vlksm) 147 147 21 357 778 73.340 46.383 31 22 Jun 2008 29 Aug 2008
4 Печатнов В.В. (pvv) 146 146 21 354 257 19.426 6.326 28 02 Jul 2008 29 Aug 2008
5 Держальцев В.А. (MadVet) 142 146 21 347 1128 60.815 28.482 28 18 May 2008 15 Aug 2008
6 Любченко В.А. (IAS56) 142 146 21 347 615 403.439 373.617 28 11 May 2008 16 Aug 2008
7 Голубин Р.С. (Roman S. Golubin) 144 145 21 348 1044 92.981 58.822 25 27 Jun 2008 25 Aug 2008
8 Селезнёв А.С. (Артём С.) 145 146 21 350 290 21.239 9.420 24 24 Aug 2008 28 Aug 2008
9 Nikolaenko A.V. (Shadow77) 145 147 21 349 349 37.317 14.010 23 27 Jul 2008 01 Aug 2008
10 Мурашкин И.В. (lepton) 144 146 21 347 839 37.289 26.815 21 09 Jul 2008 22 Jul 2008
11 Солдатенков Ю.С. (SolYUtor) 144 146 21 346 819 22.703 6.102 20 14 Aug 2008 28 Aug 2008
12 Белогурова К. (Katy_Ekb) 138 143 21 335 361 10.714 4.673 18 20 May 2008 31 Jul 2008
13 Егоров А.Б. (ABEgorov) 143 144 21 344 180 12.917 8.815 18 03 Aug 2008 12 Aug 2008
14 Войнов П.Е. (pаparome) 143 146 21 343 1034 3.103 .213 17 23 Jun 2008 28 Aug 2008
15 iglbeat (iglbeat) 143 145 21 343 360 34.601 15.225 17 08 Aug 2008 25 Aug 2008
16 Северюхин Ю.А. (Venser) 134 142 21 326 335 4.925 .655 14 01 Feb 2008 04 Feb 2008
17 Борисенков Д.В. (xuser) 137 142 21 331 92 3.217 .926 14 02 May 2008 30 May 2008
18 Мишин С.А. (CepbIu) 140 142 21 335 69 9.772 2.148 14 26 Jun 2008 01 Aug 2008
19 Тарасов Д.Б. (Gavrila) 141 142 21 337 1069 24.230 2.501 14 30 Jul 2008 29 Aug 2008
20 Кувалкин К.С. (Cyrilus) 135 142 21 327 1207 13.037 2.782 14 15 Mar 2008 07 Aug 2008

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

No surname n_sel sel_all sel_scores dml_scores scores rating last_visit
1 >Balagaev A.Y. (Toxa number 768) 67 67 125 34 159 591 29 Aug 2008
2 >Соколов А.В. (hdd-killer) 44 44 82 13 95 1772 29 Aug 2008
3 >Yakimansky V. (axfreelancer) 37 96 81 0 81 242 29 Aug 2008
4 >Gulina M.V. (proliv) 40 40 76 0 76 2578 29 Aug 2008
5 >Гребенников Д. (Dimah.A.Heinz) 40 40 75 0 75 2626 29 Aug 2008
6 lucifer (lucifer) 19 60 33 34 67 801 28 Aug 2008
7 Шитов А.Н. (sand1k) 31 45 66 0 66 2269 28 Aug 2008
8 >Зайцев (Usurer) 33 33 62 0 62 3422 29 Aug 2008
9 >Емельянов В.О. (emelyanov) 26 39 60 0 60 2679 29 Aug 2008
10 Ряховский С.В. (fix4fix) 15 53 30 20 50 1088 29 Aug 2008
11 >Пятаков (Gluttton) 27 27 48 0 48 4623 29 Aug 2008
12 >Евтисов С.С. (Minimal) 5 24 12 32 44 2612 29 Aug 2008
13 Cosmo (Cosmo) 24 24 44 0 44 5155 27 Aug 2008
14 Фомин А.В. (chelentano) 23 23 39 0 39 5708 28 Aug 2008
15 >Немо (Log) 15 31 37 0 37 3848 29 Aug 2008
16 >Семионенко Д.Л. ([tur0k]) 16 26 36 0 36 4915 29 Aug 2008
17 spanache A.M. (sanamaria2001) 14 35 32 2 34 2136 27 Aug 2008
18 Kazika D. (denis_bug) 18 18 34 0 34 6265 26 Aug 2008
19 >Гришнов Е.Н. (Mr_Smith) 4 55 8 25 33 870 29 Aug 2008
20 >Чубенко А.В. (alexteam) 17 43 33 0 33 1740 29 Aug 2008
21 >Горшунов С. (_gsv_) 22 22 33 0 33 6392 29 Aug 2008
22 >Федотов Д.Е. (DmitryFedotov) 22 22 33 0 33 6409 29 Aug 2008
23 Zaretsky P. (Zaretsky_Petr) 21 21 31 2 33 6411 29 Aug 2008
24 >Мишкин С.В. (sergeymishkin) 15 69 29 3 32 920 29 Aug 2008
25 >Kov А.С. (Alen-ka) 17 55 31 0 31 1569 29 Aug 2008
26 Гончаров Д.С. (int18h) 8 28 23 8 31 3033 27 Aug 2008
27 >Зрюмов П.А. (Paul Clever) 13 31 30 0 30 4006 29 Aug 2008

Изучаем SQL

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

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

Возвращаясь к главному, теперь, когда мы уже увидели, насколько аналитические функции упрощают код ранжирующих вычислений, давайте оценим влияние этих функций производительность. Используем простое вычисление номера строки как основу для этого сравнения. Данные в таблице 3-7 - результаты запроса, который возвращает заказы и присваивает им номера в порядке возрастания qty с использованием ROW_NUMBER.

---------------------------------------------------------------------
--  Производительность для номера строки
--  ROW_NUMBER SQL Server 2005
---------------------------------------------------------------------

DROP INDEX idx_UC_orderdate_orderid
ON dbo.Orders

CREATE UNIQUE CLUSTERED INDEX idx_UC_qty_orderid
  ON dbo.Orders(qty,orderid);


SELECT orderid, qty,
  ROW_NUMBER () OVER (ORDER BY qty) AS rownum
FROM dbo.Orders
ORDER BY qty;
GO
---------------------------------------------------------------------
Orderid qty rownum
30001 10 1
10001 10 2
10006 10 3
40005 10 4
30003 15 5
30004 20 6
20002 20 7
20001 20 8
10005 30 9
30007 30 10
40001 40 11

Таблица 3-7. Номера строк, упорядоченных по qty

Рисунок 3-1. План выполнения для вычислений номера строки при помощи функции ROW_NUMBER, реализованной в SQL Server 2005

Когда мы смотрим план исполнения на рисунке 3-1, мы замечаем, что листовой уровень кластерного индекса сканируется упорядоченно. Оптимизатору нужны данные отсортированные сначала по столбцам группы (partition), а затем - по столбцам сортировки. Так как у нас имеется индекс, мы замечаем, что нашему плану не нужен оператор сортировки. Оператор Sequence Project вычисляет ранговые значения. Для каждой строки этот оператор полагается на 2 флага, обеспечиваемых предыдущими операторами, один флаг соответствует проверке, не является ли текущая запись первой в группе (partition), и если это так, то он сбросит ранговое значение. Другой флаг проверяет отличие сортируемого значения данной строки от значения в предыдущей строке, и если это так, то оператор инкрементирует ранговое значение на основании этой функции. План весьма эффективн и сканирует данные только один раз, и если данные ещё не упорядочены по индексу, то сортирует их. Теперь давайте сравним его с вариантом запроса для SQL Server 2000. Для простоты в примере вычисляется номер строки по единственному уникальному столбцу - orderid:

---------------------------------------------------------------------
--  Производительность для номера строки
--  ROW_NUMBER SQL Server 2000
---------------------------------------------------------------------

SELECT orderid,
 (SELECT COUNT (*)
  FROM dbo.Orders AS O2
  WHERE O2.orderid <= O1.orderid) AS rownum
FROM dbo.Orders AS O1
ORDER BY orderid;

GO
---------------------------------------------------------------------

Рисунок 3-2. План выполнения для вычисления номера записи в SQL Sqerver 2000

Этот метод гораздо медленнее чем функция ROW_NUMBER. Это видно по плану выполнения на рисунке 3-2. Сначала выполняется полное сканирование по индексу на orderid , и для каждой возвращаемой строки оператор вложенных циклов (nested loop) генерирует номера строк путом их подсчета. Каждое вычисление номера строки вызывает оператор поиска (seek) в индексе по orderid, с последующим частичным сканированием с головы связанного списка листового уровня до последней точки, где orderid из внутреннего запроса меньше или равен значению orderid внешнего запроса.

Обычно одним из главных факторов, влияющих на производительность запросов манипуляции данными, является ввод-вывод. Как было показано ранее, для вычисления номеров n строк данные будут сканироваться 1+2+3+….+n = (n+ n2)/2 раз. Проблемы не возникнут сразу, но для таблицы в 100.000 строк полное число сканированных строк составит порядка 5 миллиардов! При отсутствии индекса для того, чтобы вычислить номер каждой строки, нужно просмотреть всю таблицу целиком. Полное число просканированных строк в этом случае будет порядка n2. Падение производительности растет экспоненциально. Для той же таблицы в 100.000 строк без индекса запрос завершится после сканирования 10 миллиардов записей.

Теперь давайте посмотрим на решение, основанное на курсорах. Вообще говоря, работы с курсорами следует избегать, поскольку это связано с большими накладными расходами, сказывающимися на производительности. Однако в данном случае, если размер разбиения не слишком маленький, основанное на курсорах решение выполняется лучше предыдущего, поскольку данные просматриваются всего один раз. Это означает, что по мере того, как таблица растет, решение, основанное на курсорах имеет линейное ухудшение производительности, в отличие от эспоненциального ухудшения (n2) для предыдущего решения.

---------------------------------------------------------------------
-- Вычисление номера строки
-- Решение с помощью курсора SQL Server 2000
---------------------------------------------------------------------

DECLARE @OrdersRN TABLE(RowNum INT, Orderid INT, qty INT);
DECLARE @RowNum AS INT, @OrderID INT, @qty INT;

DECLARE rncursor CURSOR FAST_FORWARD FOR
  SELECT orderid, qty
  FROM dbo.Orders ORDER BY qty;
OPEN rncursor;

SET @RowNum = 0;

FETCH NEXT FROM rncursor INTO @orderid, @qty;
WHILE @@fetch_status = 0
BEGIN
  SET @RowNum = @RowNum + 1;
  INSERT INTO @OrdersRN(RowNum, orderid, qty)
    VALUES(@RowNum, @OrderID, @qty);
  FETCH NEXT FROM rncursor INTO @OrderID, @qty;
END

CLOSE rncursor;
DEALLOCATE rncursor;

SELECT orderid, qty, RowNum FROM @OrdersRN;
GO
---------------------------------------------------------------------

Наконец, давайте посмотрим на вычисление номера записи с помощью функции IDENTITY.

---------------------------------------------------------------------
-- Производительность: Вычисление номера записи
-- Использование Identity SQL Server 2000
---------------------------------------------------------------------
-- SELECT INTO without ORDER BY
IF OBJECT_ID('tempdb..#Orders') IS NOT NULL
  DROP TABLE #Orders;
GO

SELECT IDENTITY(int, 1, 1) AS RowNum,
  orderid + 0 AS orderid, qty
INTO #Orders
FROM dbo.Orders;
GO

-- CREATE TABEL w/IDENTITY, INSERT SELECT … ORDER BY
DECLARE @OrdersRN TABLE( OrderID INT, qty INT,RowNum INT IDENTITY);
INSERT INTO @OrdersRN(OrderID, qty)
  SELECT OrderID, qty
  FROM dbo.Orders
  ORDER BY qty;
SELECT * FROM @OrdersRN;
GO

Использование функции IDENTITY в операторе SELECT INTO будет самым быстрым способом вычисления номера записи в SQL Server до версии 2005, потому что данные сканируются только один раз и отсутствуют накладные расходы, связанные с использованием курсоров. В добавок SELECT INTO минимально журнализируемая операция, если не используется полная модель восстановления (FULL recovery model). Однако этот способ можно использовать только если порядок назначения номера строки не важен, как в нашем примере. Если номера строк опираются на заданный порядок, SELECT INTO использовать не следует. Сначала нужно создать таблицу, и только потом её заполнять.

Подводя итог, можно смело утверждать, что для вычисления ранговых значений функция ROW_NUMBER с предложением OVER гораздо быстрее любого метода имеющегося в арсенале SQL Server 2000. Оно не только быстрее, но исключительно понятно и просто.

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

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

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

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

§ Желающих поспособствовать популяризации сайта прошу проголосовать/поставить закладку в социальных сетях:
del.icio.us
dzone.com
Digg.com
stumbleupon.com

Контакты

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

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

В избранное