Сообщений 1    Оценка 52 [+2/-0]         Оценить  
Система Orphus

SQL. Задачи и решения

Автор: С. Моисеенко
Издательство: Питер, 2006
256 страниц

Материал предоставил: Издательство ''Питер''
Найти в магазинах
Купить в Озоне (223 руб.)
Купить в Books.Ru
Купить в Болеро (207 руб.)
Купить в издательстве "Питер"
Купить в My-Shop.ru (209 руб.)

Аннотация

Содержание
Введение
О чем эта книга
Что необходимо для работы с книгой
Как читать эту книгу
Используемая терминология и особенности реализации
Благодарности
Глава 2. База данных "Фирма вторсырья"
Упражнение 20
Упражнение 21
Упражнение 22

Аннотация

Идея этой книги родилась из опыта сопровождения сайта http://www.sql-ex.ru, где в режиме онлайн можно решать задачи на написание запросов к учебным базам данных. Автор не ставит целью формально описать все аспекты этого языка. Наоборот, он старается донести до читателя самую суть и восполнить пробел поверхностного изложения синтаксиса глубоким проникновением в логику построения запросов. Основной материал книги составляет анализ ошибочных решений задач. Эти ошибки не являются надуманными. Запросы писали посетители сайта, которые после неудачных попыток решить проблему просили объяснить, почему верный, по их мнению, запрос не принимается системой проверки. Поэтому можно смело утверждать, что в книге разбираются характерные ошибки и их объяснение позволит добиться значительно большего прогресса в изучении SQL, чем простое рассмотрение примеров использования тех или иных конструкций языка. Книга предназначена для всех, кого интересует глубокое практическое изучение приемов программирования на SQL.

Содержание


Введение
О чем эта книга
Что необходимо для работы с книгой
Как читать эту книгу
Используемая терминология и особенности реализации
Благодарности
От издательства

Часть I. Характерные ошибки при решении упражнений на выборку данных (оператор SELECT)

Глава 1. База данных ''Компьютерная фирма''

Упражнение 1
Упражнение 2
Упражнение 3
Упражнение 4
Упражнение 5
Упражнение 6
Упражнение 7
Упражнение 8
Упражнение 9
Упражнение 10
Упражнение 11
Упражнение 12
Упражнение 13
Упражнение 14
Упражнение 15
Упражнение 16
Упражнение 17
Упражнение 18
Упражнение 19

Глава 2. База данных ''Фирма вторсырья''

Упражнение 20
Упражнение 21
Упражнение 22

Глава 3. База данных ''Корабли''

Упражнение 23
Упражнение 24
Упражнение 25
Упражнение 26
Упражнение 27
Упражнение 28
Упражнение 29
Упражнение 30
Упражнение 31
Упражнение 32
Упражнение 33
Упражнение 34
Упражнение 35
Упражнение 36

Часть II. Язык манипуляции данными в SQL

Глава 4. Оператор SELECT

Простой оператор SELECT
Предикаты I
Переименование столбцов и вычисления в результирующем наборе
Предикаты II
Получение итоговых значений
Использование в запросе нескольких источников записей
Традиционные операции над множествами и оператор SELECT
Использование ключевых слов SOME|ANY и ALL с предикатами сравнения
Преобразование типов и оператор CAST
Оператор CASE

Глава 5. Операторы модификации данных

Оператор INSERT
Оператор UPDATE
Оператор DELETE

Часть III. Готовимся ко второму этапу тестирования

Глава 6. Функции Transact-SQL для работы со строками и данными типа даты/времени

Функции T-SQL для работы со значениями даты и времени
Функции работы со строками в SQL SERVER 2000

Глава 7. Типичные проблемы

Генерация числовой последовательности
Нумерация
Об использовании оператора CASE
Еще раз о NULL-значениях
Трехзначная логика и предложение WHERE
Эти ''хитрые'' внешние соединения
О неявном преобразовании типов в SQL Server 2000
Случайная выборка строк из таблицы в SQL Server 2000
Коррелирующие подзапросы
Заключение

Приложение 1. Описание учебных баз данных

Компьютерная фирма
Фирма вторсырья
Корабли
Аэрофлот
Окраска

Приложение 2. Список упражнений

Приложение 3. Подсказки и решения

Упражнение 2
Упражнение 5
Упражнение 6
Упражнение 7
Упражнение 8
Упражнение 9
Упражнение 11
Упражнение 12
Упражнение 13
Упражнение 14
Упражнение 15
Упражнение 18
Упражнение 19
Упражнение 20
Упражнение 21
Упражнение 22
Упражнение 23
Упражнение 24
Упражнение 25
Упражнение 27
Упражнение 28
Упражнение 29
Упражнение 30
Упражнение 31
Упражнение 32
Упражнение 33
Упражнение 34
Упражнение 35

Список цитируемых источников
Алфавитный указатель

Введение

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

Цель этой книги - быстрое изучение языка SQL. При этом "быстрое" вовсе не означает "поверхностное". Напротив, оставляя в стороне многие теоретические аспекты языка, автор старается дать глубокое понимание логической структуры данных и, как следствие, правильного построения запросов с учетом этой структуры. Надеемся, что после чтения этой книги вам будет легче понять и то, о чем здесь не написано.

О чем эта книга

Книга посвящена решению упражнений по извлечению информации из реляционной базы данных, то есть наиболее синтаксически сложному оператору SELECT. Здесь вы также найдете необходимую справочную информацию по другим операторам подъязыка манипуляции данными (DML - Data Manipulation LanguageDML, язык), а именно операторам INSERT, UPDATE и DELETE, осуществляющих модификацию данных.

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

Упражнения взяты с сайта Упражнения по SQL, который уже на протяжении нескольких лет эффективно используется в системе дистанционного обучения языку SQL.

Следуя испытанной практике, предлагаем вам поучиться на чужих ошибках. При этом, разбирая ошибочные решения, в большинстве случаев не дается окончательных "правильных" решений. Это вызвано двумя причинами:

Справедливости ради, заметим, что после выяснения причины ошибки в результате анализа неверных решений их исправление не должно составить труда.

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

Что необходимо для работы с книгой

Для решения рассматриваемых в книге упражнений вы можете:

  1. Применить любую имеющуюся или создать новую базу данных практически на любом SQL-сервере (Oracle, MS SQL Server, IBM DB2), запустить скрипты, которые можно взять по адресу http:// msi77.narod.ru/downld.html. Каждый из этих скриптов создаст необходимые таблицы учебной базы данных и наполнит их данными. Выполнить скрипт можно с помощью клиентской части соответствующего сервера, там же и будут выполняться запросы-решения. Для SQL Server такой клиентской частью является Query AnalyzerQuery Analyzer, входящий в дистрибутив сервера. Собственно, все скрипты тестировались именно с помощью SQL Server 2000, хотя следование стандарту ANSI SQL (SQL92) позволяет использовать аналогичные продукты других производителей.
  2. Если вы новичок и информация из первого пункта вам мало понятна, просто зайдите на сайт www.sql-ex.ru, где в режиме онлайн можно запускать все представленные в книге скрипты и решать не только опубликованные здесь упражнения. Кроме того, реализованная на сайте система проверки будет контролировать правильность ваших решений, а имеющийся форум позволит познакомиться с решениями других посетителей. Вы можете пройти хорошую школу, разобравшись в опубликованных на форуме решениях, так как многие из них писались профессионалами.

Как читать эту книгу

Если вы только приступаете к изучению языка SQL, то лучше начать с части II, где рассматривается синтаксис оператора SELECT и приведены номера упражнений, которые рекомендуется решить для закрепления соответствующего материала. Затем переходите к части I, которая поможет преодолеть затруднения при решении упражнений и разобраться в нюансах. С операторами модификации данных также можно познакомиться в части II. Упражнения по этим операторам не рассматриваются в книге, поскольку они достаточно просты, и сложные конструкции могут возникать здесь только за счет использования подзапросов. Множество примеров в книге и решение упражнений на сайте позволят вам в полной мере усвоить этот материал.

Если вы уже знакомы с языком SQL, то начинайте с части I, обращаясь к части II лишь для получения справки по тем или иным предложениям оператора SELECT или чтобы узнать о специфических особенностях реализации (в настоящее время мы используем MS SQL Server 2000).

В части III книги приводится описание функций Transact-SQL - языка, предназначенного для программирования серверной части приложений в SQL Server и SyBase, а также изложены типичные проблемы программирования на языке SQL. Эти знания пригодятся для решения задач сертификационного этапа на сайте. На данном этапе уже трудно обойтись без особенностей реализации и знания характерных приемов программирования. Надеемся, что эта информация также окажется полезной для вас.

Советуем начинать чтение с описания предметной области, моделируемой учебной базой данных, и внимательного изучения схемы данных. Затем стоит познакомиться с "неправильным" решением и самостоятельно найти в нем ошибку, а затем прочитать объяснение.

Используемая терминология и особенности реализации

Будем считать синонимами термины таблица и отношение; строка и кортеж столбец, атрибут и поле. Интересующихся тонкостями терминологии отсылаем к фундаментальной книге Дейта [1].

Там, где требуются специфические возможности (функции), не оговоренные стандартом, при написании запросов применяется синтаксис SQL Server (версии 2000). Все приведенные в книге скрипты проверялись именно на этой СУБД, которая также используется на сайте с упражнениями. Справочную информацию по этим специфическим особенностям вы найдете в части III.

Благодарности

В первую очередь автор хотел бы поблагодарить своих коллег, помогавших создавать и сопровождать сайт, - Майстренко А. В., Лысенко О. В., Калинкина В. Ю. и Валуева Д. И. Без них не было бы ни сайта, ни книги. Автор также выражает признательность Гершовичу В. И., который первым предложил написать данную книгу. Автор благодарит всех авторов задач, лиц, внесших свой вклад в развитие ресурса, а также многочисленных посетителей, ошибки которых здесь и анализируются. Невозможно перечислить их всех поименно, так как список этих людей стал бы самой большой частью книги. Их имена всегда можно увидеть на сайте www.sql-ex.ru.

Две схемы данных и многие формулировки задач по этим схемам заимствованы из книги Дж. Ульмана и Дж. Уидома [2], которым автор выражает свою признательность.

Глава 2. База данных "Фирма вторсырья"

Фирма занимается приемом вторсырья и имеет несколько пунктов приема. Каждый пункт получает деньги для их выдачи сдатчикам в обмен на сырье. Фактически на схеме представлены две базы данных. В каждой задаче по этой схеме используется только одна пара таблиц (либо с приставкой _o, либо без нее).

В таблицах Income_o и Outcome_o первичным ключом является пара атрибутов {point, date} - номер пункта приема и дата. Этот ключ должен моделировать ситуацию, когда сведения о получении денег на приемном пункте и их выдаче сдатчикам записываются в базу данных не чаще одного раза в день.


Рис. 2.1. Схема базы данных ''Фирма вторсырья''

Значения данных в столбце date не содержат времени, например 2001-03-22 00:00:00.000. К сожалению, использование для этого столбца типа данных datetime может вызвать непонимание, поскольку очевидно, что учет времени не позволит ограничить многократный ввод значений с одной и той же датой (и номером пункта), но отличающихся временем дня. Этот недостаток, связанный с отсутствием отдельных типов данных для даты и времени, уже преодолен в версии SQL Server 2005. При использовании же SQL Server 2000 обеспечить правильность ввода можно при помощи, например, следующего ограничения (CK_Income_o):

ALTER TABLE Income_o ADD 
CONSTRAINT PK_Income_o PRIMARY KEY 
     (
      [point],
      [date]
      ),
CONSTRAINT CK_Income_o CHECK 
     (
      DATEPART(hour,[date]) + DATEPART(minute,[date]) + 
      DATEPART(second,[date]) + DATEPART(millisecond,[date]) = 0
      )

Это ограничение (сумма часов, минут, секунд и миллисекунд равна нулю) не позволит ввести какое-либо время, отличное от 00:00:00.000. При таком ограничении первичный ключ на данной таблице будет действительно гарантировать наличие лишь одной записи в день для каждой точки.

Таблица Income_o (point, date, inc) содержит информацию о поступлении денежных сумм (inc) на пункт приема (point). Аналогичная таблица - Outcome_o (point, date, out) - служит для контроля расхода денежных средств (out).

Вторая пара таблиц - Income (code, point, date, inc) и Outcome (code, point, date, out) - моделирует ситуацию, когда приход и расход денег может фиксироваться несколько раз в день. Следует отметить, что если записывать в последние таблицы только дату без времени (что и имеет место), то никакая естественная комбинация атрибутов не может служить первичным ключом, поскольку суммы денег также могут совпадать. Поэтому нужно либо учитывать время, либо добавить искусственный ключ. Мы использовали второй вариант, добавив целочисленный столбец code только для того, чтобы обеспечить уникальность записей в таблице.

Упражнение 20

Предположив, что приход и расход денег на каждом пункте приема фиксируется произвольное число раз (в обе таблицы добавлен первичный ключ code), написать запрос с выходными данными (point, date, out, inc), в котором каждому пункту за каждую дату соответствует одна строка.

В этой задаче требуются данные из двух таблиц собрать в одном результирующем наборе; при этом приход и расход денег на пункте приема в один и тот же день должны находиться в одной строке.

Аналогичное упражнение 29 для таблиц Income_o и Outcome_o, размещенное на сайте, как правило, затруднений не вызывало. Суть проблемы демонстрирует следующее решение:

SELECT Income.point, Income.date, SUM(out), SUM(inc)
FROM Income LEFT JOIN 
     Outcome ON Income.point = Outcome.point AND
                Income.date = Outcome.date
GROUP BY Income.point, Income.date
UNION
SELECT Outcome.point, Outcome.date, SUM(out), SUM(inc)
FROM Outcome LEFT JOIN 
     Income ON Income.point = Outcome.point AND
               Income.date = Outcome.date
GROUP BY Outcome.point, Outcome.date

Идея решения такова. Выполняется соединение таблицы, в которой фиксируются приходы денег, с таблицей расходов средств по совпадению номера пункта приема и даты. Левое соединение, которое здесь используется, гарантирует получение результата в том случае, если на пункте приема в некоторые дни есть только приход, но нет расхода (NULL). Далее выполняется объединение с запросом, в котором выполняется обратное левое соединение таблицы расхода с таблицей прихода. Таким образом, учитывается случай, когда на пункте есть расход, но нет прихода. Исключение дубликатов строк (в случаях, когда есть и приход, и расход) выполняется использованием оператора UNION.UNION, оператор

Запрос дает неверный результат, когда в один день на пункте приема выполняется несколько операций по приходу/расходу денежных средств. В качестве примера возьмем характерный для этого случая день - 24 марта 2001 года. Выполним пару запросов:

SELECT * FROM Income WHERE date = '2001-03-24 00:00:00.000' AND point = 1
SELECT * FROM Outcome WHERE date = '2001-03-24 00:00:00.000' AND point = 1

Получим следующий результат:

Приход
codepointDateinc
312001-03-24 00:00:00.0003600.0000
1112001-03-24 00:00:00.0003400.0000
Расход
codepointDateout
212001-03-24 00:00:00.0003663.0000
1312001-03-24 00:00:00.0003500.0000

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

incout
3600.00003663.0000
3600.00003500.0000
3400.00003663.0000
3400.00003500.0000

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

И дубликаты здесь ни при чем, так как каждый из объединяемых запросов дает аналогичный результат, то есть остается одна строка на каждую пару значений пункт, дата.

Упражнение 21

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

SELECT ss.point, ss.inc - dd.out
FROM (SELECT i.point, SUM(inc) AS inc
      FROM Income_o i 
      GROUP BY i.point
      ) AS ss,
      (SELECT o.point, SUM(out) AS out
       FROM Outcome_o o 
       GROUP BY o.point
       ) AS dd
WHERE ss.point = dd.point

В предложении FROM в каждом из подзапросов определяется сумма соответственно прихода и расхода денежных средств на каждом из пунктов приема. Эти подзапросы соединяются по равенству номеров пунктов приема, что позволяет построчно вычислить остаток денежных средств на каждом пункте: ss.inc - dd.out.

Казалось бы, все правильно, однако решение содержит одну ошибку. Попробуйте ее найти.

Упражнение 22

Посчитать остаток денежных средств на начало дня 15.04.2001 на каждом пункте приема для базы данных с отчетностью не чаще одного раза в день. Вывод: пункт, остаток.

SELECT i.point, CASE inc 
                     WHEN NULL 
                     THEN 0 
                     ELSE inc 
                END - 
                CASE out 
                     when NULL 
                     THEN 0
                     ELSE out 
                END
FROM (SELECT point, SUM(inc) inc
      FROM Income_o
      WHERE '20010415' > date
      GROUP BY point
      ) AS I FULL JOIN 
     (SELECT point, SUM(out) out 
      FROM Outcome_o
      WHERE '20010415' > date
      GROUP BY point
      ) AS III ON III.point = I.point

Это упражнение во многом аналогично предыдущему упражнению 5921. По сути, здесь дополнительно применяется лишь отбор по дате. В связи с этим обращаем внимание на ее представление в запросе. Дело в том, что в предикате сравнивается строка с полем типа datetime. В SQL Server имеется функция CONVERT,CONVERT(), функция которая позволяет преобразовать строковое представление даты/времени к этому типу, используя различные форматы представления даты. Однако строковое представление в виде год месяц день, которое используется в рассматриваемом решении, всегда будет правильно преобразовываться неявно к типу datetime вне зависимости от настроек сервера [4].

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

    Сообщений 1    Оценка 52 [+2/-0]         Оценить