пятница, 8 февраля 2019 г.

Обработка запросов к БД при помощи PEAR::XML

Обработка запросов к БД при помощи PEAR::XML

Автор: Дмитрий Лебедев
Проект, работающий на технологиях XML, требует иного подхода к формированию документов. Инструменты, сделанные для формирования HTML-документов, часто оказываются непригодными, и требуются новые, имеющие несколько иную концепцию.
Проект, работающий на технологиях XML, требует иного подхода к формированию документов. Инструменты, сделанные для формирования HTML-документов, часто оказываются непригодными, и требуются новые, имеющие несколько иную концепцию.
Скажем, как после добавления в проект поддержки XSLT формировать в XML данные, получаемые из базы? Оказывается, что делать это при помощи класса шаблона ничуть не проще, чем составлять документ прямо в скрипте:
<?php

$result = mysql_query("SELECT DATE_FORMAT(news_date, '%e.%c.%Y') AS date_typed, 
 DATE_FORMAT(news_date, '%d.%m.%Y') AS date_url, title, announce FROM news 
 ORDER BY news_date DESC LIMIT 10");

if(!mysql_error()) {
    $xml .= "\r\n";

    while($row = mysql_fetch_assoc($result))
        $xml .= "
            
                {$row[date_url]}
                {$row[date_typed]}
                
                {$row[announce]}
            \r\n";

    $xml .= "\r\n";
    }

?>
Согласитесь, с классом шаблона мы получили бы примерно тот же объём кода (а ещё файлы .tpl на диске).
Очевидно, что всё, что можно было вывести из скриптов php, уже выведено. Дата в приведённом примере форматируется базой данных и возвращается сразу в нужном нам формате, форматирование с подсветкой четных строк, нумерацией и ещё чем-нибудь делаются в XSLT. Код, которым мы выбираем данные из базы, максимально упрощён и повторяется из раза в раз. Возникает естественное желание сжать его в минимальную конструкцию.
Размышление: Если вы хотите решить какую-то проблему на php, поищите в архивах скриптов — скорее всего для вашей задачи найдётся уже готовое решение. Чужой скрипт, конечно, не будет идеальным решением (потому что наилучшим приближением к идеалу будет решение, написанное специально для задачи), но, скорее всего, будет пригодно к использованию. Вы можете быть противником применения чужого кода в своих проектах, либо не быть уверены в том, что найденный код будет работать как надо (разработчики PEAR, как известно, предупреждают, что весь их проект — вечная бета). Однако посмотреть, как чужой скрипт работает, будет полезно, чтобы написать свой собственный. Заглянув в чужой код, и профессиональный разработчик, и разработчик среднего уровня смогут лучше понять подходы к решению задачи и написать своё собственное, если это понадобится.
В конце сентября, прочитав присланный мне материал про PEAR::DB, я решил поискать на PEAR и класс для автоматического преобразования результатов запроса к базе данных в XML. В разделе XML я нашёл класс, созданный как раз для моей проблемы — XML_sql2xml.
Класс использует для доступа к базам данных другой класс библиотеки PEAR — PEAR::DB. Для операций с XML-документом использует функции DOM XML (это требует установки соответствующего модуля php). Преобразует результат запроса в XML-дерево и возвращает его либо как текст XML-документа, либо как DOM-объект.
Установка PEAR
Тема не была серьёзно описана в материале о PEAR::DB, поэтому будем разбираться.
Библиотеки PEAR раньше распространялись с дистрибутивом php, но больше этого не будет — видимо, чтобы устаревшие версии классов не распространялись с дистрибутивами php, их оттуда убрали. Теперь архивы с классами нужно брать с сервера pear.php.net.
Проблемы с установкой PEAR вызваны тем, что во всех его файлах указываются пути относительно корня директории pear, то есть для вызова PEAR::DB делается команда
include_once("DB/${type}.php");
PHP, если не найден подключаемый файл, пытается искать его в своей директории или в директории, куда установлен PEAR. Однако не все его устанавливают на тестовой машине, а чтобы исключить проблемы несовместимости и отсутствия PEAR на хостинге, многие предпочли бы положить необходимые файлы библиотеки в поддиректорию разрабатываемого проекта.
Как вы могли увидеть из строки кода выше, файлы подключаются относительно include_path (который по умолчанию является директорией, из которой работает скрипт, либо директорией, в которую установлен PEAR). Если в проекте include_path устанавливается, вам повезло. Можно положить вызываемые скрипты в эту директорию и наслаждаться жизнью.
Если include_path не устанавливается, его можно установить в .htaccess такой строкой:
php_value include_path my_dir/pear
Вместо my_dir поставьте адрес нужной директории. Можно поменять include_path "на лету" в скрипте:
<?php

ini_set("include_path", "my_dir/pear");

include("DB.php");

$dsn = "mysql://user:pass@host/db_name";
$db = DB::connect($dsn, true);

if (DB::isError($db)) {
 die ($db->getMessage());
}

$db->setFetchMode(DB_FETCHMODE_ASSOC);

ini_restore("include_path");

?>
После подключения файлов PEAR лучше вернуть include_path на место командой ini_restore. В комментариях к предыдущей статье меня спросили, зачем нужен ini_restore. Во-первых, у меня все скрипты работают из корня сайта, а подключаемые файлы находятся в разных директориях (например, содержимое страницы сайта может быть статичным XML-файлом, а может быть скриптом, который будет вызываться основными скриптами). Пути к подключаемым файлам я указываю тоже от корня. PEAR на сайте появилась не сразу, поэтому ini_restore нужен, чтобы подключить библиотеку, и это не мешало остальным скриптам работать по-старому. Во-вторых, в php.ini или в .htaccess может указываться путь к директории с подключаемыми файлами, а PEAR, по-моему, лучше положить в отдельную директорию, чтобы библиотека не захламляла рабочую. Впрочем, каждый делает как ему удобнее.
Однако хостинг-провайдер может держать php в безопасном режиме, который запрещает менять include_path. В таком случае, конечно, можно попробовать положить файлы PEAR в корень сайта или вручную править все include в файлах — больше ничем разработчики помочь не могут. В новостях PEAR пишут, что Стиг Баккен сообщил о плане добавить новую директиву '{get,set,restore}_include_path()', чтобы можно было менять iclude_path даже в безопасном режиме.
DOM XML в PHP
DOM (Document Object Model) — модель работы с документом, в которой документ содержит объекты, которыми можно манипулировать. Модель DOM является стандартом W3C. Функции DOM XML в php — это одна из реализаций данной модели.
При работе с DOM вы оперируете с переменными, являющимися ссылками на объекты нескольких классов. Список классов и их функций можно узнать в соответствующем разделе руководства по php.
Стандартный модуль php_domxml не поддерживает кириллицу. При работе с ним вам придётся конвертировать данные на входе в UTF-8, а на выходе — обрабатывать сущности вроде &x440;. Чтобы установить DOM XML с поддержкой кириллицы на рабочей машине под Win32, рекомендую скачать мой архив, в котором находится модуль php_domxml скомпилированный с поддержкой кириллицы и необходимые для его работы библиотеки iconv, libxml, libxslt и libexslt. Положите файл php_domxml.dll в extenstion_dir, а остальные библиотеки — в директорию c:\windows\system.
Несколько примеров по работе с DOM XML:
<?php

// Создание XML-документа
$xmldoc = domxml_new_doc("1.0");

// В условиях документа создаётся элемент под названием my_root. 
$my_element = $xmldoc->create_element("my_root");

// Затем этот элемент присоединяется к документу как узел-потомок. До этой операции в 
//документе нет корневого узла!
$my_root = $xmldoc->append_child($my_element);

// Создаётся ещё один элемент — текст и добавляется как потомок к корневому узлу.
$my_element = $xmldoc->create_text_node(iconv("windows-1251", "UTF-8", "Это содержимое 
//корневого узла XML-документа."));
$my_root->append_child($my_element);

// XML-документ преобразуется в текстовый вид и выводится
print($xmldoc->dump_mem());

?>
К сожалению, в приведённом примере возникнут проблемы с русскими символами — на выходе они опять превращаются в &xXXX;. Чтобы модуль domxml понял, что идёт работа с русской кодировкой, нужно на входе дать XML-документ с параметром encoding="windows-1251" вот так:
<?php

// Создание объекта документа из текстовой строки
$xmldoc = domxml_open_mem('<?xml version="1.0" encoding="windows-1251"?>');

// Cсылку на корневой узел документа записываем в переменную $my_root (название 
//переменной значения не имеет).
$my_root = $xmldoc->document_element();

// Создаём текстовый узел.
$my_element = $xmldoc->create_text_node(iconv("windows-1251", "UTF-8", 
 "Это содержимое корневого узла XML-документа."));

// Присоединяем текстовый узел к корневому.
$my_root->append_child($my_element);

print($xmldoc->dump_mem());

?>
Следующий пример показывает, как можно удалять элементы:
<?php

$xmldoc = domxml_open_mem('<?xml version="1.0" encoding="windows-1251"?>
Нечто
');

$my_root = $xmldoc->document_element();

// В массив $children записываются все потомки узла my_root.
$children = $my_root->child_nodes();

// Уничтожается первый потомок (узел something)
$children[0]->unlink_node();

// Создаётся новый узел под названием new и записывается в переменную $new
$new = $my_root->append_child($xmldoc->create_element("new"));

// К этому узлу добавляется потомок — текстовый узел
$new->append_child($xmldoc->create_text_node(iconv("windows-1251", "UTF-8", 
 "Это содержимое нового узла XML-документа.")));

print($xmldoc->dump_mem());

?>
в результате получится такой XML-документ:
<?xml version="1.0" encoding="windows-1251"?>
<my_root><new>Это содержимое нового узла XML-документа.</new></my_root>
Кстати, если изменить исходный XML-документ на такой:
<?php

$xmldoc = domxml_open_mem('<?xml version="1.0" encoding="windows-1251"?>

    Нечто

');

?>
Результат будет иным:
<?xml version="1.0" encoding="windows-1251"?>
<my_root><something>Нечто</something>
<new>Это содержимое нового узла XML-документа.</new></my_root>
Предоставляю вам возможность догадаться, почему это произошло.
Конечно же, приведённые примеры — самое простое из того, что можно делать в DOM XML. Кроме построения нового это и самые хитрые преобразования документа, и XSL-трансформация при помощи библиотеки libxslt, не уступающей в функциональности Sablotron, а в скорости превосходящей его в два раза. Перед нами открываются огромные возможности по работе с документом, проблема — как организовать и систематизировать эту работу.
На ум приходит следующая схема преемника классов шаблонов: вызывается скрипт, который открывает стандартный XML-файл и включает буферизацию данных. Все скрипты тупо выдают XML-данные в print. Вызывается второй скрипт, который останавливает буферизацию, берёт данные из буфера, дописывает к ним в начале "<?xml version="1.0" encoding="windows-1251"?><root>" и "</root>" в конце, затем преобразует в объект DOM, открывает корневой элемент и берёт всех массив потомков. Полученные узлы вставляет в основной XML-документ (который тоже открыт как объект), результат преобразует через XSLT и выдаёт пользователю.
На этом заканчиваем краткое описание DOM XML и переходим к классу sql2xml.
Класс SQL2XML
Вся функциональность, которая нужна для преобразования результатов SQL-запросов в XML, есть в этом классе. Для соединения с базой данных класс использует либо существующее соединение класса PEAR::DB, либо своё собственное (точнее, он создаёт в себе объект класса DB). Из результата запроса строится XML-дерево. Пример из руководства по классу:
mysql> select * from bands;
+----+--------------+------------+-------------+-------------+
| id | name         | birth_year | birth_place | genre       |
+----+--------------+------------+-------------+-------------+
|  1 | The Blabbers |       1998 | London      | Rock'n'Roll |
|  2 | Only Stupids |       1997 | New York    | Hip Hop     |
+----+--------------+------------+-------------+-------------+

mysql> select * from albums;
+----+---------+------------------+------+-----------------+
| id | bandsID | title            | year | comment         |
+----+---------+------------------+------+-----------------+
|  1 |       1 | BlaBla           | 1998 | Their first one |
|  2 |       1 | More Talks       | 2000 | The second one  |
|  3 |       2 | All your base... | 1999 | The Classic     |
+----+---------+------------------+------+-----------------+
Это набор данных. А теперь вызов класса и результаты работы. php-код:
<?php
include_once("XML/sql2xml.php");
$sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$xmlstring = $sql2xmlclass->getxml("select * from bands");
?>
XML-документ:
<?xml version="1.0"?>
    <root>
        <result>
            <row>
                <id>1</id>
                <name>The Blabbers</name>
                <birth_year>1998</birth_year>
                <birth_place>London</birth_place>
                <genre>Rock'n'Roll</genre>
            </row>
            <row>
                <id>2</id>
                <name>Only Stupids</name>
                <birth_year>1997</birth_year>
                <birth_place>New York</birth_place>
                <genre>Hip Hop</genre>
            </row>
        </result>
    </root>
Результат выводится и в текстовом виде, и как DOM-объект (что весьма удобно при генерации документов через DOMXML). Так же можно из всего XML-дерева выдернуть одно значение при помощи выражения XPath. Ещё очень хорошая особенность: раз уж строятся деревья, и всё оперируется в XML, почему бы запросы с объединением "один-ко-многим" не делать в виде вложенных друг в друга узлов <row>. php-код:
<?php
include_once("XML/sql2xml.php");
$sql2xml = new xml_sql2xml("mysql://username:password@localhost/xmltest");
$xmlstring = $sql2xml->getxml("select * from bands left join albums on bands.id = bandsID");
?>
XML-документ:
<?xml version="1.0"?>
    <root>
        <result>
            <row>
                <id>1</id>
                <name>The Blabbers</name>
                <birth_year>1998</birth_year>
                <birth_place>London</birth_place>
                <genre>Rock'n'Roll</genre>
                <row>
                    <id>1</id>
                    <bandsID>1</bandsID>
                    <title>BlaBla</title>
                    <year>1998</year>
                    <comment>Their first one</comment>
                </row>
                <row>
                    <id>2</id>
                    <bandsID>1</bandsID>
                    <title>More Talks</title>
                    <year>2000</year>
                    <comment>The second one</comment>
                </row>
            </row>
            <row>
                <id>2</id>
                <name>Only Stupids</name>
                <birth_year>1997</birth_year>
                <birth_place>New York</birth_place>
                <genre>Hip Hop</genre>
                <row>
                    <id>3</id>
                    <bandsID>2</bandsID>
                    <title>All your base...</title>
                    <year>1999</year>
                    <comment>The Classic</comment>
                </row>
            </row>
        </result>
    </root>
Впрочем, если вы хотите получить обычный результат запроса, это свойство можно отключить. Если имена узлов для результата и для ряда вас не устраивают, можно их поменять. Если вас не устраивает формат (всё в текстовых узлах, а не, например, в атрибутах), можно преобразовать полученный DOM-объект в нужный вам. На мой взгляд, этого не понадобится, поскольку если на сайте до этого уже использовался XSLT, исправить XSL-файл не представляет особой сложности.
Итак, класс вполне пригоден к использованию. Если он вас чем-то не устраивает, можно, глядя на существующий, написать свой собственный. Исправлять данный класс вполне можно, поскольку манипуляции с DOM-объектами не намного сложнее внутреннего устройства классов шаблонов. Я для себя исправил ошибки call-time pass-by-reference в классе версии 0.3 (версия 0.3.1 — это как раз мой багфикс) а так же заменил старые не поддерживаемые функции и конструкции DOM XML на новые. Сейчас работаю над тем, как справиться с проблемой кодировки документа (объект документа там создаётся функцией domxml_new_doc, а для создания из текстовой строки требуется основательно переделать существующую в классе sql2xml функцию).
Ссылки по теме

Оператор выбора SELECT

Оператор выбора SELECT

Язык запросов (Data Query Language) в SQL состоит из единственного оператора SELECT. Этот единственный оператор поиска реализует все операции реляционной алгебры. Как просто, всего один оператор. Однако писать запросы на языке SQL (грамотные запросы) сначала совсем не просто. Надо учиться, так же как надо учиться решать математические задачки или составлять алгоритмы для решения непростых комбинаторных задач. Один и тот же запрос может быть реализован несколькими способами, и, будучи все правильными, они, тем не менее, могут существенно отличаться по времени исполнения, и это особенно важно для больших баз данных.
Синтаксис оператора SELECT имеет следующий вид:
SELECT [ALL | DISTINCT] «писок полей>|*)
FROM <Список таблиц>
[WHERE <Предикат-условие выборки или соединения>]
[GROUP BY <Список полей результата>]
[HAVING <Предикат-условие для группы>]
[ORDER BY <Список полей, по которым упорядочить вывод>]
Здесь ключевое слово ALL означает, что в результирующий набор строк включаются все строки, удовлетворяющие условиям запроса. Значит, в результирующий набор могут попасть одинаковые строки. И это нарушение принципов теории отношений (в отличие от реляционной алгебры, где по умолчанию предполагается отсутствие дубликатов в каждом результирующем отношении). Ключевое слово DISTINCT означает, что в результирующий набор включаются только различные строки, то есть дубликаты строк результата не включаются в набор.
Символ *. (звездочка) означает, что в результирующий набор включаются все столбцы из исходных таблиц запроса.
В разделе FROM задается перечень исходных отношений (таблиц) запроса.
В разделе WHERE задаются условия отбора строк результата или условия соединения кортежей исходных таблиц, подобно операции условного соединения в реляционной алгебре.
В разделе GROUP BY задается список полей группировки.
В разделе HAVING задаются предикаты-условия, накладываемые на каждую группу.
В части ORDER BY задается список полей упорядочения результата, то есть список полей, который определяет порядок сортировки в результирующем отношении. Например, если первым полем списка будет указана Фамилия, а вторым Номер группы, то в результирующем отношении сначала будут собраны в алфавтном порядке студенты, и если найдутся однофамильцы, то они будут расположены в порядке возрастания номеров групп.
В выражении условий раздела WHERE могут быть использованы следующие предикаты:
  • Предикаты сравнения { =, <>, >,<, >=,<= }, которые имеют традиционный смысл.
  • Предикат Between A and В — принимает значения между А и В. Предикат истинен, когда сравниваемое значение попадает в заданный диапазон, включая границы диапазона. Одновременно в стандарте задан и противоположный предикат Not Between A and В, который истинен тогда, когда сравниваемое значение не попадает в заданный интервал, включая его границы.
  • Предикат вхождения в множество IN (множество) истинен тогда, когда сравниваемое значение входит в множество заданных значений. При этом множество значений может быть задано простым перечислением или встроенным подзапросом. Одновременно существует противоположный предикат NOT IN (множество), который истинен тогда, когда сравниваемое значение не входит в заданное множество.
  • Предикаты сравнения с образцом LIKE и NOT LIKE. Предикат LIKE требует задания шаблона, с которым сравнивается заданное значение, предикат истинен, если сравниваемое значение соответствует шаблону, и ложен в противном случае. Предикат NOT LIKE имеет противоположный смысл.
    По стандарту в шаблон могут быть включены специальные символы:
    • символ подчеркивания (_) — для обозначения любого одиночного символа;
    • символ процента (%) — для обозначения любой произвольной последовательности символов;
    • остальные символы, заданные в шаблоне, обозначают самих себя.
  • Предикат сравнения с неопределенным значением IS NULL. Понятие неопределенного значения было внесено в концепции баз данных позднее. Неопределенное значение интерпретируется в реляционной модели как значение, неизвестное на данный момент времени. Это значение при появлении дополнительной информации в любой момент времени может быть заменено на некоторое конкретное значение. При сравнении неопределенных значений не действуют стандартные правила сравнения: одно неопределенное значение никогда не считается равным другому неопределенному значению. Для выявления равенства значения некоторого атрибута неопределенному применяют специальные стандартные предикаты:
    <имя атрибута>IS NULL и <имя атрибута> IS NOT NULL.
Если в данном кортеже (в данной строке) указанный атрибут имеет неопределенное значение, то предикат IS NULL принимает значение «Истина» (TRUE), а предикат IS NOT NULL — «Ложь» (FALSE), в противном случае предикат IS NULL принимает значение «Ложь», а предикат IS NOT NULL принимает значение «Истина».
Введение Null-значений вызвало необходимость модификации классической двузначной логики и превращения ее в трехзначную. Все логические операции, производимые с неопределенными значениями, подчиняются этой логике в соответствии с заданной таблицей истинности:
А
В
Not A
А ^ В
 B
TRUE
TRUE
FALSE
TRUE
TRUE
TRUE
FALSE
FALSE
FALSE
TRUE
TRUE
Null
FALSE
Null
TRUE
FALSE
TRUE
TRUE
FALSE
TRUE
FALSE
FALSE
TRUE
FALSE
FALSE
FALSE
Null
TRUE
FALSE
Null
Null
TRUE
Null
Null
TRUE
Null
FALSE
Null
FALSE
Null
Null
Null
Null
Null
Null

  • Предикаты существования EXIST и несуществования NOT EXIST. Эти предикаты относятся к встроенным подзапросам, и подробнее мы рассмотрим их, когда коснемся вложенных подзапросов.
В условиях поиска могут быть использованы все рассмотренные ранее предикаты.
Отложив на время знакомство с группировкой, рассмотрим детально первые три строки оператора SELECT:
  • SELECT — ключевое слово, которое сообщает СУБД, что эта команда — запрос. Все запросы начинаются этим словом с последующим пробелом. За ним может следовать способ выборки — с удалением дубликатов (DISTINCT) или без удаления (ALL, подразумевается по умолчанию). Затем следует список перечисленных через запятую столбцов, которые выбираются запросом из таблиц, или символ '*' (звездочка) для выбора всей строки. Любые столбцы, не перечисленные здесь, не будут включены в результирующее отношение, соответствующее выполнению команды. Это, конечно, не значит, что они будут удалены или их информация будет стерта из таблиц, потому что запрос не воздействует на информацию в таблицах — он только показывает данные.
  • FROM — ключевое слово, подобно SELECT, которое должно быть представлено в каждом запросе. Оно сопровождается пробелом и затем именами таблиц, используемых в качестве источника информации. В случае если указано более одного имени таблицы, неявно подразумевается, что над перечисленными таблицами осуществляется операция декартова произведения. Таблицам можно присвоить имена-псевдонимы, что бывает полезно для осуществления операции соединения таблицы с самой собою или для доступа из вложенного подзапроса к текущей записи внешнего запроса (вложенные подзапросы здесь не рассматриваются).
Все последующие разделы оператора SELECT являются необязательными.
Самый простой запрос SELECT без необязательных частей соответствует просто декартову произведению. Например, выражение
SELECT *
FROM Rl. R2
соответствует декартову произведению таблиц R1 и R2. Выражение
SELECT Rl.A, R2.B
FROM Rl. R2
соответствует проекции декартова произведения двух таблиц на два столбца А из таблицы R1 и В из таблицы R2, при этом дубликаты всех строк сохранены, в отличие от операции проектирования в реляционной алгебре, где при проектировании по умолчанию все дубликаты кортежей уничтожаются.
  • WHERE — ключевое слово, за которым следует предикат — условие, налагаемое на запись в таблице, которому она должна удовлетворять, чтобы попасть в выборку, аналогично операции селекции в реляционной алгебре.
Рассмотрим базу данных, которая моделирует сдачу сессии в некотором учебном заведении. Пусть она состоит из трех отношений R1, R2, R3. Будем считать, что они представлены таблицами Rl, R2 и R3 соответственно.
R1 = (ФИО, Дисциплина, Оценка);
R2 = (ФИО, Группа);
R3 = (Группы, Дисциплина )
R1
ФИО
Дисциплина
Оценка
Петров Ф. И.
Базы данных
5
Сидоров К. А.
Базы данных
4
Миронов А. В.
Базы данных
2
Степанова К. Е.
Базы данных
2
Крылова Т. С.
Базы данных
5
Сидоров К. А.
Теория информации
4
Степанова К. Е.
Теория информации
2
Крылова Т. С.
Теория информации
5

R1
ФИО
Дисциплина
Оценка
Миронов А. В.
Теория информации
Null
Владимиров В. А.
Базы данных
5
Трофимов П. А.
Сети и телекоммуникации
4
Иванова Е. А.
Сети и телекоммуникации
5
Уткина Н. В.
Сети и телекоммуникации
5
Владимиров В. А.
Английский язык
4
Трофимов П. А.
Английский язык
5
Иванова Е. А.
Английский язык
3
Петров Ф. И.
Английский язык
5

R2

ФИО
Группа
Петров Ф. И.
4906
Сидоров К. А.
4906
Миронов А. В.
4906
Крылова Т. С.
4906
Владимиров В. А.
4906
Трофимов П. А.
4807
Иванова Е. А.
4807
Уткина Н. В.
4807

R3
Группа
Дисциплина
4906
Базы данных
4906
Теория информации
4906
Английский язык
4807
Английский язык
4807
Сети и телекоммуникации
Приведем несколько примеров использования оператора SELECT.
  • Вывести список всех групп (без повторений), где должны пройти экзамены.
SELECT DISTINCT Группы
FROM R3
Результат:
Группа
4906
4807

  • Вывести список студентов, которые сдали экзамен по дисциплине «Базы данных» на «отлично».
SELECT ФИО
FROM R1
WHERE Дисциплина = "Базы данных" AND Оценка = 5
Результат:
ФИО
Петров Ф. И.
Крылова Т. С.
Вывести список всех студентов, которым надо сдавать экзамены с указанием названий дисциплин, по которым должны проводиться эти экзамены.
SELECT ФИО,Дисциплина
FROM R2.R3
WHERE R2.Группа = R2.Группа;
Здесь часть WHERE задает условия соединения отношений R2 и R3, при отсутствии условий соединения в части WHERE результат будет эквивалентен расширенному декартову произведению, и в этом случае каждому студенту были бы приписаны все дисциплины из отношения R3, а не те, которые должна сдавать его группа.
Результат:
ФИО
Дисциплина
Петров Ф. И.
Базы данных
Сидоров К. А.
Базы данных
Миронов А. В.
Базы данных
Степанова К. Е.
Базы данных
Крылова Т. С.
Базы данных
Владимиров В. А.
Базы данных
Петров Ф. И.
Теория информации
Сидоров К. А.
Теория информации
Миронов А. В.
Теория информации
Степанова К. Е.
Теория информации

ФИО
Дисциплина
Крылова Т. С.
Теория информации
Владимиров В. А.
Теория информации
Петров Ф. И.
Английский язык
Сидоров К. А.
Английский язык
Миронов А. В.
Английский язык
Степанова К. Е.
Английский язык
Крылова Т. С.
Английский язык
Владимиров В. А.
Английский язык
Трофимов П. А.
Сети и телекоммуникации
Иванова Е. А.
Сети и телекоммуникации
Уткина Н. В.
Сети и телекоммуникации
Трофимов П. А.
Английский язык
Иванова Е. А.
Английский язык
Уткина Н. В.
Английский язык

  • Вывести список лентяев, имеющих несколько двоек.
SELECT DISTINCT R1.ФИО
FROM Rl a, R1 b
WHERE a.ФИО = b.ФИО AND
a.Дисциплина <>
b.Дисциплина AND
а.Оценка <= 2 AND b.Оценка <= 2:
Здесь мы использовали псевдонимы для именования отношения R, а и b, так как для записи условий поиска нам необходимо работать сразу с двумя экземплярами данного отношения.
Результат:
ФИО
Степанова К. Е.
Из этих примеров хорошо видно, что логика работы оператора выбора (декартово произведение—селекция—проекция) не совпадает с порядком описания в нем данных (сначала список полей для проекции, потом список таблиц для декартова произведения, потом условие соединения). Дело в том, что SQL изначально разрабатывался для применения конечными пользователями, и его стремились сделать возможно ближе к языку естественному, а не к языку алгоритмическому. По этой причине SQL на первых порах вызывает путаницу и раздражение у начинающих его изучать профессиональных программистов, которые привыкли разговаривать с машиной именно на алгоритмических языках.
Наличие неопределенных (Null) значений повышает гибкость обработки информации, хранящейся в БД. В наших примерах мы можем предположить ситуацию, когда студент пришел на экзамен, но не сдавал его по некоторой причине, в этом случае оценка по некоторой дисциплине для данного студента имеет неопределенное значение. В данной ситуации можно поставить вопрос: «Найти студентов, пришедших на экзамен, но не сдававших его с указанием названия дисциплины». Оператор SELECT будет выглядеть следующим образом:
SELECT ФИО. Дисциплина
FROM R1
WHERE Оценка IS NULL
Результат:

ФИО
Дисциплина
Миронов А. В.
Теория информации