Новости сайта "Упражнения по SQL" (http://www.sql-ex.ru) 97
Новости сайта "Упражнения по SQL (http://www.sql-ex.ru)" Выпуск 97 (22 июля 2006 г.)
Новым посетителям сайта
Сайт посвящен изучению языка, с помощью которого осуществляется взаимодействие с реляционными (и не только) СУБД. Суть обучения состоит в выполнении заданий на написание запросов к учебным базам данных; при этом система контролирует правильность выполнения заданий. В настоящее время реализованы все операторы подъязыка манипуляции данными (DML), которые включают в себя оператор извлечения данных SELECT, а также операторы модификации данных - INSERT, DELETE и UPDATE.
Мы надеемся, что справочного материала сайта окажется достаточно для самостоятельного обучения. Кроме того, свои решения вы можете обсудить на форуме сайта. Опытных же специалистов приглашаем проверить (продемонстрировать) свое мастерство и принять участие в соревновании, обеспечиваемом рейтинговой системой учета времени выполнения заданий. Фактически, рейтинг ведется на втором этапе тестирования, который начинается сейчас после решения 58-ти задач первого этапа. При подсчете рейтинга каждого
участника отбрасывается один самый худший показатель среди всех решенных им упражнений.
Демонстрация плана выполнения запроса и сравнительная оценка эффективности решений поможет вам освоить принципы оптимизации запросов.
Имеется возможность получить сертификат по SQL DML при выполнении определенного количества заданий.
Новости сайта
§ Подлатал проверку задачи 93. Прохождение неверных решений заметил Roman S. Golubin.
§ Дособирал отзывы о проекте. Получилось 2 странички. Мне бы хотелось, чтобы все отзывы были подписаны, но некоторые из них я брал с форума SQL.RU, а у некоторых моих респондентов устарел почтовый ящик. Обращаюсь с просьбой к тем, кто увидит свой отзыв на этих двух страничках, сообщить информацию о себе, естественно, если вы не против публикации. Я старался собрать
все отзывы, в которых проявлялась заинтересованность к ресурсу, а не только хвалебные. Некоторые предложения уже реализованы в той или иной мере, по некоторым замечаниям приняты исправительные меры.
§ Подрезал число аксакалов. Годы идут, и 365 дней уже не кажутся таким большим числом. Поставил 600. Напомню, что 600 - это число дней, которые прошли между первым и последним решенными упражнениями. Вторым критерием здесь является преодоление первого сертификационного порога, проще говоря, 65 задач на SELECT. Попасть в аксакалы не так и сложно, т.к. новые задачи добавляются регулярно. Зато я увеличил список претендентов
на попадание в ТОР 100. Теперь сюда входят те, кто решил минимум 66 задач на SELECT, причем последняя решенная задача датируется текущим или предыдущим месяцем.
§ Возвращение лидера. Победив 138 задачу и установив рекорд на 87-ой, на первое место вернулся paparome. Результат (задач 138, время 2.682) недостижим для ближайших преследователей. Похоже, что интригу можно возродить только на третьем этапе, до которого пока руки не дошли. Впрочем, есть еще несколько новых задач в запасе, однако, большинство из них опять же принадлежит paparome :-). На третье место переместился lepton (138, 10.102). Остальные лидеры или выжидают,
или пока не решили 87 задачу.
§ Проверка английских формулировок закончена: a2010 принялся за последнюю 138 задачу. Мне думается, что он сможет ее решить. Возможно, что мы увидим его в десятке. Кажется, соревнование приобретает международный статус :-).
§ Сохранили шансы попасть в ТОР 10: ValdemarES (137, 7.807) a2010 (137, 15.365) =Maxim= (121, 11.802) maxifly (115, 2.186)
§ Продолжили свое восхождение к вершине: Phohack (135, 129.167) SoVa (124, 94.307) ba (122, 38.315) FanOfBeer (120, 72.267) T! (115, 36.418) Johnny (114, 130.073) Ocean (108, 27.741)
§ На этой неделе сертифицированы: Оля (A06008771) [BK] (г.Элиста, Россия) Roman S. Golubin (B06005936) [AR] (г.Северодвинск, Россия) Platon (A06010718) [BK] (г.Ижевск, Россия)
Вы безумный? Не сердитый, а скорее сумасшедший, когда дело доходит до проектирования баз данных на SQL Server? Дон Петерсон встречал нескольких человек, которые, как он думает, именно таковы, когда дело доходит до построения поисковых таблиц. Проистекает ли это от плохого понимания проектирования базы данных? Или Вы не согласны? Прочитайте возражения Дона против такой методики проектирования.
За эти годы я участвовал во множестве "дискуссий" о практике создания обобщенных "поисковых" таблиц для хранения многочисленных типов кодов в единственной таблице. Я не буду держать вас в неизвестности относительно моего взгляда на эти методы, основа которых лежит в отказе от изучения и признания ценности фундаментальных принципов хорошего проектирования базы данных. Соответственно, я придумал подходящее название для таких таблиц: Массово унифицированные ключевые таблицы кодов (Massively
Unified Code-Key tables ) или MUCK (переводится как НАВОЗ - прим. переводчика).
Давайте рассмотрим причины, почему некоторые люди испытывают желание использовать этот подход, но, что еще более важно, я надеюсь объяснить, почему Вам не следует его использовать. Многие "поисковые" таблицы выглядят примерно как эти примеры:
CREATE TABLE EmployeeStatusCode ( EmployeeStatusCode int IDENTITY(1,1) , Description varchar(50))
CREATE TABLE CustomerStatusCode ( CustomerStatusCode int IDENTITY(1,1) , Description varchar(50))
CREATE TABLE FacilityStatusCode ( FacilityStatusCode int IDENTITY(1,1) , Description varchar(50))
* К слову, я не люблю термин "Поисковые" таблицы, ни любой из его синонимов (кодовая таблица - Code Table, доменная таблица - Domain Table, справочная таблица - Reference Table и т.д ...), т.к. он слишком неоднозначен, чтобы быть полезным. Как точно Вы их определите? Является ли это просто некоторая таблица, на которую есть ссылка по внешнему ключу? Очевидно, нет. А как насчет таблицы, на которую ссылается FK и которая не ссылается ни на какую другую таблицу? Нет... Даже при том, что я использую
этот термин в данной статье, я хочу отметить, что эти таблицы существенно не отличаются от любых других таблиц в вашей базе данных и поэтому не должны иметь никакой специальной трактовки*
** Для ясности..., я не защищаю повсеместное использование столбцов IDENTITY (или вообще суррогатных ключей), т.к. эта тема требует намного более детального рассмотрения, чем я намереваюсь дать в этой статье. Я использую этот пример только потому, что он является очень распространенным. **
Поскольку коды состояния служащего (Employee), клиента (Customer) и средства (Facility) выглядят одинаково, утверждается, что с точки зрения "определенной перспективы" они имеют один и тот же тип и должны посему находиться в одной таблице. Это кажется не лишенным смысла до тех пор, пока Вы не поймете, что совместное использование ряда общих атрибутов - это только один из критериев, которые должны рассматриваться при определении таких вещей. К сожалению, эта "определенная перспектива",
при которой ваши кодовые таблицы могут соответственно быть объединены, также делает их неэффективными для использования по их оригинальному назначению - обеспечению целостности данных. Даже поверхностная проверка должна сказать нам, что EmployeeStatusCode не ТО ЖЕ САМОЕ, что и CustomerStatusCode, независимо от того, насколько ПОДОБНЫ они по своему внешнему виду. Мы никогда бы не захотели, чтобы состояние клиента (Customer) было "Fired" (запущен) или состояние служащего - "Vacant" (свободен)
независимо от того, насколько это описание может подходить для некоторых из них :-). Есть несколько альтернативных возможностей воспрепятствовать этому, но, как я покажу далее, каждая из них создает еще большие проблемы.
Как правило, во время процесса нормализации (нет, нормализация не является здесь грязным словом) эти коды удаляются из таблиц Employee, Customer и Facility и соответственно помещаются в их собственные таблицы. Затем некто обращает внимание на подобие кодовых таблиц и решает, что база данных будет более простой, если объединить их в единственную таблицу. Нормализация устраняет избыточность данных, значительно упрощая, тем самым, задачу поддержания целостности данных, но процесс создания MUCK никак не связан
с целостностью. MUCK не устраняет избыточные данные, скорее они есть устранение того, что ВОСПРИНИМАЕТСЯ как избыточные таблицы, но, как я продемонстрирую, меньшее число таблиц не эквивалентно простоте.
Вы можете сказать: "Этот парень не знает того, о чем говорит! MUCK действительно помогает устранить избыточные данные в тех случаях, когда EmployeeStatusCode использует то же самое описание, что и CustomerStatusCode и так далее." Прекрасно, но это лишь послужит в дальнейшем иллюстрацией, почему Вы не должны трактовать эти коды как вещи одного и того же типа. Не требуется длительного размышления, чтобы понять, что эта логика быстро ведет нас обратно в уродливый мир аномалий обновления и другого
нереляционного уродства. То есть. Что, если требования бизнеса приводят к изменению этого специфического описания, но только для служащих? Возможно это? Несомненно, но в чем проблема?
Как упомянуто выше, как только Вы получаемее MUCK, возникает проблема, говорящая: какие ограничения наложить на данные, чтобы гарантировать применимость кодов типа X исключительно к X? Первый вариант, о котором задумывается масса людей, - это триггеры. Добавьте столбец CodeType к MUCK и напишите соответствующие триггеры. Просто!..., но прежде чем остановиться на этом подходе, продолжим читать. Если Вы не хотите использовать триггеры для поддержания целостности, Вы могли бы попробовать включить столбец
CodeType в таблицу Employee с ограничением на столбец, которое предотвращает любые коды состояния, которые не относятся к типу служащего.
Рассмотрим пример:
CREATE TABLE Code ( CodeID int IDENTITY(1,1) , CodeType int , Description varchar(255))
ALTER TABLE Code ADD CONSTRAINT PK_Code PRIMARY KEY (CodeID, CodeType)
Очевидно, мы могли бы иметь таблицу CodeType ..., но с другой стороны, почему просто не впихнуть это в нашу таблицу Code? В конце концов, она выглядит точно так же, как все наши остальные коды! Есть место для каждого на борту этой рекурсивной поездки на американских горках!
Наша таблица Employee выглядела бы примерно так:
CREATE TABLE Employee ( EmployeeID int IDENTITY(1,1) , FirstName varchar(50) , LastName varchar(50) , CodeType int , CodeID int , и т.д ...) ALTER TABLE Employee ADD CONSTRAINT FK_Code FOREIGN KEY (CodeID, CodeType) REFERENCES Code (CodeID, CodeType)
ALTER TABLE Employee ADD CONSTRAINT CHK_CodeType
CHECK(CodeType = 1)
Это должно работать, правильно? Да, но при этом Вы должны пожелать не замечать слона в гостиной комнате, наша таблица служащих не нормализована должным образом, поскольку CodeID, в частности, зависит от CodeType, который не является частью ключа (и нет, Вы не можете просто сделать CodeType чатью ключа, потому что FirstName, LastName и т.д ... не зависели бы тогда от всего ключа). Теперь подумаем о том, какая образуется мешанина, если служащий может иметь более одной комбинации значений Code/CodeType.
Далее, а что если одна из этого множества пар Code/CodeType зависит от другой пары? Прежде, чем Вы побежите назад в "простоту" варианта с триггерами, следует понять, что там имеется та же самая проблема, но она сокрыта в логике триггера и вероятно потребовала кода значительного размера. Фактически можно утверждать, что триггерный вариант является худшим выбором, потому что CodeID имеет функциональную зависимость от столбца, который там даже не находится! Картина становится только более уродливой, если
Вы примете во внимание потери производительности на триггерах по сравнению с DRI (декларативная ссылочная целостность).
(Окончание следует...)
Полезная информация
§ Все статьи, публикуемые в рассылке, затем выкладываются на сайте Книги и статьи по SQL.
§ Поступила в продажу книга SQL. Задачи и решения, посвященная анализу ошибок, допускаемых при решении задач первого этапа. На сайте издательства Питер можно сделать заказ и познакомиться с содержанием.
Контакты
По всем вопросам, связанным с функционированием сайта, проблемами при решении упражнений, идеями вы можете обращаться к Сергею И.Моисеенко msi77@yandex.ru. Вы также можете предложить свои задачи для публикации на сайте.