egorius: (Default)

Начало: 1. Необходимая предыстория, назад: 3. Реляционная алгебра.

Если реляционная алгебра более или менее привычна (что не удивительно, поскольку SQL в основном базируется именно на ней), то с исчислением дело обстоит хуже. Реляционное исчисление основано не на теории множеств, а на другом формализме — логике предикатов первого порядка. Впервые Кодд подробно описал его в работе 1972 года, хотя и упоминал с самой первой статьи.

Логика предикатов корнями уходит в труды Аристотеля (а это IV век до н. э), который пытался свести рассуждения об истинности любого предположения к формальным правилам вывода новых фактов из имеющихся (силлогизмы Аристотеля).

В нынешнем понимании, логика предикатов имеет дело со следующими объектами. Во-первых, с константами и переменными. Во-вторых, с предикатами, под которыми понимаются булевские (т. е. принимающие истинное или ложное значение) функции, аргументами которых могут выступать константы и переменные. В-третьих, предикаты объединяются в выражения с помощью логических связок: и (∧), или (∨), не (¬).

Предикаты связаны с отношениями вот как. Возьмем какое-нибудь отношение, допустим, V. Неформальный смысл этого отношения можно определить так: «vendor# — поставщик с именем vendor_name». Но чтобы точно задать отношение, мы должны явно перечислить всех поставщиков и их имена. Можно считать, что отношение содержит множество истинных утверждений-кортежей: если (x, y) входит в отношение, то x — поставщик с именем y. Что, если (x, y) не входит в отношение? Тогда мы принимаем предположение о замкнутости мира и считаем, что такого поставщика нет в природе. Предикат, соответствующий отношению, показывает истинность утверждения («является ли vendor# поставщиком с именем vendor_name?»): он принимает истинное значение тогда и только тогда, когда его единственный параметр-кортеж входит в соответствующее отношение.

Предикаты-отношения я для удобства буду обозначать той же буквой, что и само отношение. Например, V(x) означает, что переменная x принимает значение из множества кортежей отношения V.

Кроме предикатов-отношений Кодд включил в реляционное исчисление и обычные бинарные предикаты сравнения (равно, больше и т. п.) для использования с атрибутами кортежей. К атрибутам можно обратиться, используя индекс: x[1] обозначает первый атрибут V.

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

  • существования — ∃x (P) означает «найдется такой x, что верно P»,
  • всеобщности — ∀x (P) означает «для любого x верно P».

Теперь можно сказать о том, почему эта логика называется логикой первого порядка. Это значит, что аргументами предикатов могут быть только переменные (кортежи), но не сами предикаты (отношения). Например, в рамках логики первого порядка нельзя сказать: «для любого отношения S верно ...». Тем не менее, как показывает практика, ее выразительной мощности вполне достаточно (интересно, что в самой первой статье Кодд предполагал необходимость использования логики второго порядка).

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

Напомню схему нашей тестовой БД и рассмотрим на ней те же самые примеры, что и для реляционной алгебры (для удобства сравнения я также приведу рядом и выражения алгебры).

  • Номер поставщика, поставляющего хоть что-нибудь:

    s[1]: S(s)                            select distinct vendor#
                                          from   S
    
    S[1]                                  select distinct vendor#
                                          from   S
    
  • Имя поставщика, не поставляющего ни одной детали:

    v[2]: V(v) ∧ ¬∃s( S(s) ∧ s[1]=v[1] )  select vendor_name
                                          from   V
                                          where  not exists
                                                (select vendor#
                                                 from   S
                                                 where  S.vendor# = V.vendor#)
    
    (V[1=1](V[1]-S[1]))[2]                select vendor_name
                                          from   V,
                                                (select vendor# from V
                                                 minus
                                                 select vendor# from S) V2
                                          where  V.vendor# = V2.vendor#
    
  • Номер поставщика, поставляющего деталь 15:

    s[1]: S(s) ∧ s[2]=15                  select vendor#
                                          from   S
                                          where  S.part# = 15
    

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

    (S[2=1]{15})[1]                       select vendor#
                                          from   S,
                                                (select 15 part# from dual) P
                                          where  S.part# = P.part#
    
  • Номер поставщика, поставляющего все детали:

    v[1]: V(v) ∧ ∀p( P(p) ∧ ∃s( S(s) ∧    select vendor# 
      ∧ s[2]=p[1] ∧ s[1]=v[1] ) )         from   V 
                                          where  not exists
                                                (select part#
                                                 from   P
                                                 where  not exists
                                                       (select part#
                                                        from   S
                                                        where  S.part# = P.part# and S.vendor# = V.vendor#) )
    
    

    В SQL нет аналога квантора всеобщности, но при принятом предположении о замкнутости мира верно ∀x (P) = ¬∃x (¬P).

    S[1]-((S[1]⊗P[1])-S)[1]               select vendor# 
                                          from   S 
                                          minus
                                          select vendor#
                                          from  (select S.vendor#, P.part# from S, P
                                                 minus
                                                 select S.vendor#, S.part# from S)
    

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

Продолжение следует: 5. Что же выбрать?

Почитать и посмотреть )

egorius: (Default)

Начало: 1. Необходимая предыстория, назад: 2. Программист-навигатор.

Итак, к концу 1960-х рынок СУБД (а рынок как таковой только начал формироваться: до этого продавали аппаратуру, а не программы) был представлен сетевой системой IDS Бахмана (General Electric), иерархической IMS (IBM) и некоторыми другими, менее известными. Несмотря на то, что сейчас мы говорим «сетевая модель» и «иерархическая модель», эти продукты разрабатывались без какой-либо четкой математической концепции; во главу угла была поставлена эффективность кода.

Примерно в этот момент (1968 год) базами данных заинтересовался Эдгар Кодд, работавший в IBM. Его идея была в том, что привлечь новых заказчиков можно только простым и понятным способом извлечения информации из баз данных, а такой способ должен опираться на стройную модель. Будучи математиком, он заметил связь между хранимыми в БД данными и логическими высказываниями. Через год работы появился внутренний отчет IBM, а в начале 1970 — знаменитая статья в Communications of ACM.

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

Центральным понятием реляционной теории является отношение (relation) — то, что обычно мы называем таблицей (иногда ошибочно полагают, что под отношением имеется в виду связь между двумя таблицами, путая, возможно, с entity-relationship diagram). Отношение является множеством кортежей (tuple), состоящих из атрибутов (attribute). Для каждого атрибута определен тип данных; количество атрибутов определяет степень (или арность) отношения. В обычной терминологии кортеж отношения — это строка таблицы, а атрибут — столбец.

Однако несмотря на внешнее сходство отношений и таблиц, надо понимать, что отношения, будучи множествами, обладают важными свойствами, не всегда верными для таблиц SQL:

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

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

Кодд предложил два способа работы с отношениями. Первый — реляционная алгебра — использует тот факт, что отношения являются множествами (хоть и специального вида), а, значит, к ним применимы такие традиционные теоретико-множественные операции, как объединение (∪), пересечение (∩) и разность (−). Единственная тонкость в том, что для этих операций отношения должны иметь одинаковый набор атрибутов — иначе результат будет множеством, но перестанет быть отношением.

На SQL эти операции обозначаются UNION (но не UNION ALL!), INTERSECT и MINUS.

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

Проекция оставляет из всего набора атрибутов некоторое их подмножество. Например, проекция отношения A на 2 и 5 атрибуты обозначается A[2,5].

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

Соединение двух отношений по условию равенства общих атрибутов порождает новое отношение, кортежи которого являются конкатенациями всевозможных пар кортежей исходных отношений, для которых выполняется это условие. Например, если у отношений A и B общими считаются атрибуты A[1] и B[3], то соединение обозначается A[1=3]B.

Такое соединение называют еще эквисоединением. Частный его случай, когда два отношения не имеют общих атрибутов, называется расширенным декартовым произведением: A⊗B.

В SQL соединения формулируются во фразе FROM. Особенно отчетливо это видно в ANSI-синтаксисе, где условия соединения выписываются явно, а в традиционном синтаксисе условия переходят во фразу WHERE.

Ограничение оставляет лишь те кортежи, которые удовлетворяют определенному условию на атрибуты. Например, ограничение отношения A по условию, что атрибут 1 больше, чем атрибут 2: A[1 > 2].

Ограничения в SQL записываются во фразе WHERE.

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

Рассмотрим несколько запросов на следующем, почти классическом, примере: имеются поставщики (V — VENDORS) и детали (P — PARTS), связаные многие-ко-многим поставками (S — SUPPLIES).

Рядом с выражением реляционной алгебры для пущей ясности приводится непосредственный аналог на SQL.

  • Номер поставщика, поставляющего хоть что-нибудь:

    S[1]                            select distinct vendor#
                                    from   S
    
  • Имя поставщика, не поставляющего ни одной детали:

    (V[1=1](V[1]-S[1]))[2]          select vendor_name
                                    from   V,
                                          (select vendor# from V
                                           minus
                                           select vendor# from S) V2
                                    where  V.vendor# = V2.vendor#
    

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

  • Номер поставщика, поставляющего деталь 15:

    (S[2=1]{15})[1]                 select vendor#
                                    from   S,
                                          (select 15 part# from dual) P
                                    where  S.part# = P.part#
    

    Выражение в фигурных скобках представляет собой литерал отношения, отсутствующий в SQL — его пришлось имитировать выборкой из dual.

  • Номер поставщика, поставляющего все детали:

    S[1]-((S[1]⊗P[1])-S)[1]         select vendor# 
                                    from   S 
                                    minus
                                    select vendor#
                                    from  (select S.vendor#, P.part# from S, P
                                           minus
                                           select S.vendor#, S.part# from S)
    

    Такую операцию Кодд даже рассмотрел отдельно, введя для нее имя реляционного деления.

В заключение дам слово Дону Чемберлину. «Тед (друзья Эдгара Кодда называли его Тедом) организовал симпозиум в исследовательском отделе IBM. Это было мое первое знакомство с мощью математического подхода к управлению базами данных. Я довольно плотно изучал интерфейс CODACYL и мог представить себе, как будет выглядеть запрос, когда надо получить что-то из базы данных... Пришел Тед и начал придумывать из головы запросы и писать их на доске на своем реляционном языке. И они получались очень короткие и понятные. ... С той поры мне уже не хотелось больше работать с CODASYL, потому что я увидел потенциал реляционного подхода».

Продолжение следует: 4. Реляционное исчисление.

Почитать и посмотреть )

egorius: (Default)

Говоря о истории языка SQL, на первый взгляд кажется логичным начать с 1969 года, в конце которого Эдгар Фред Кодд, работавший в исследовательском отделении IBM в Сан-Хосе, написал свой отчет, положивший начало реляционной теории, а вместе с ней и реляционным базам данных.

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

История развития вычислительной техники начиная с изобретения абака — это так или иначе история автоматизации вычислений. Даже само слово «вычислительная» говорит об этом (и в английском языке тоже: «computer», «calculator» — вычислитель).

Важная переломная точка приходится на XIX век. Типичной задачей того времени, требовавшей большого объема вычислений, был расчет различных таблиц (например, логарифмических или тригонометрических). Работа над устройствами, способными автоматически справляться с вычислениями, привела Чарльза Бэббиджа к его знаменитой ныне Аналитической машине. Другие устройства того времени были спроектированы для решения какой-либо одной конкретной задачи, Аналитическая же машина была по задумке Бэббиджа программируема.

Она состояла из блоков, присутствующих теперь в любой ЭВМ: арифметическое устройство, запоминающее устройство, управляющее устройство, периферия (ввод программы и данных с жаккаровских перфокарт и вывод результатов на печать). К сожалению, Аналитическая машина никогда не была построена, хотя это и неудивительно: за неимением лучшего в середине XIX века, Бэббидж спроектировал свою машину на механической элементной базе (то есть в прямом смысле на рычагах и шестеренках).

Однако заложенные в машину принципы оказались настолько удачными, что они во многом определили дальнейшее развитие вычислительной техники. Говард Эйкен, создавший в 1943 году (при участии IBM — так уж получилось, что эта корпорация будет встречаться нам постоянно) первую реально работавшую программируемую машину Automatic Sequence Controlled Calculator (для краткости названную Mark I), признался: «Живи Бэббидж на 75 лет позже, я остался бы безработным». Фактически Mark I повторял архитектуру Аналитической машины, но на более современной элементной базе (Эйкен использовал как механические элементы, как и электромеханические реле).

Аналитическая машина, к слову говоря, оказала влияние и на программирование. Благодаря как самому Бэббиджу, так и Аде Лавлейс (дочери «того самого» лорда Байрона) мы получили такие понятия, как цикл, условный переход и библиотека подпрограмм.

Некоторое время после появления ЭВМ их продолжали использовать только для вычислительных задач (в основном с военной спецификой: расчет баллистических таблиц, вычисления для создания водородной бомбы, криптография). Но уже к концу 1940-х появилось понимание, что наряду с чисто математическими задачами, программируемый компьютер может решать и задачи бизнеса. Первыми ласточками стали великобританский EDSAC (1949) и построенные в начале 1950-х на его основе коммерческая система LEO (применялась для расчета зарплаты, складского учета, планирования производства — практически ERP), а также американский UNIVAC (1951), созданный для хранения, обработки и печати большого объема данных (в качестве внешней памяти использовалась магнитная лента емкостью порядка миллиона символов, лентопротяжное устройство обеспечивало скорость 12500 символов в минуту).

Более широкому применению ЭВМ в бизнесе мешало отсутствие запоминающего устройства большой емкости с произвольным доступом: магнитная лента по своей сути последовательна, а магнитные барабаны имели небольшую емкость и использовались в основном как дополнительное «медленное» ОЗУ. Ситуация изменилась в 1956 году с изобретением IBM жестких магнитных дисков и появлением первого НЖМД IBM 350 Disk File (устройство имело емкость 5 миллионов символов и состояло из 50 дисков, вращающихся со скоростью 1200 оборотов в минуту).

«Появление запоминающих устройств прямого доступа стало причиной переворота в мышлении... Направления, обозначаемые словами в и из, поменялись на обратные. Если директива ввод в мире последовательного доступа к файлам означала с ленты в вычислительную машину, то новая директива ввод означает в базу данных» — писал Чарльз Бахман, создатель первой СУБД.

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

Однако многими важными для информационных систем свойствами ОС все-таки не обладают. Взаимосвязанные данные сложной структуры нужно хранить в нескольких файлах, обеспечивая при этом их согласованность (пользователь БД не должен увидеть «промежуточной» ситуации, когда в одном файле информация уже изменилась, а в другом еще нет). Должна быть возможность одновременного доступа к данным многими пользователями (как для чтения, так и для изменения), а механизмы файловых систем для этого слишком грубы. Требуется надежное хранение информации с возможностью восстановления базы данных после сбоя в согласованном состоянии. Кроме того, нужен пользовательский интерфейс к базе данных для выборки и изменения информации.

Решать эти задачи каждый раз заново в каждом приложении, работающем с базой данных, совершенно неинтересно. Поэтому уже в 1961-64 годах появилась первая система управления базами данных, IDS (Integrated Data Store), спроектированная упоминавшимся выше Чарльзом Бахманом. Идеологию этой СУБД Бахман принес с собой и в комитет CODASYL (Conference on Data System Languages).

Первоначальной задачей комитета, объединившего в 1959 году представителей многих компаний, связанных с обработкой данных, являлась разработка универсального языка программирования для решения задач, ориентированных на бизнес. К началу 1960-х уже существовал ряд языков высокого уровня, из которых стоит упомянуть Фортран (разработан Джоном Бэкусом для решения вычислительных задач в 1954 году в исследовательском отделении IBM в Сан-Хосе), Алгол (разработан в 1958 году комитетом европейских и американских ученых, включая и Бэкуса; имел академическую направленность), Лисп (первый функциональный язык; создан в 1958 году Джоном Маккарти из MIT).

Результатом работы CODASYL явился новый язык Кобол (COBOL, Common Business-Oriented Language). Позднее, в 1965 году, в составе CODASYL при участии Бахмана выделилась группа по базам данных, которая выработала сетевую модель баз данных. Команды для работы с БД интегрировали в Кобол — аналогичным образом 20 лет спустя Oracle интегрирует SQL в PL/SQL.

За работу по созданию первой СУБД и за участие в работе CODASYL, Бахман в 1973 году стал лауреатом премии Тьюринга, вручаемой ежегодно с 1966 года ассоциацией ACM «исследователю, выбранному за полезные сообществу программистов и пользователей достижения технического характера».

Продолжение: 2. Программист-навигатор.

Почитать и посмотреть )

Profile

egorius: (Default)
egorius

March 2025

M T W T F S S
      1 2
34 567 89
1011 121314 1516
17181920212223
24252627 28 29 30
31      

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 29th, 2025 08:15 pm
Powered by Dreamwidth Studios