В настоящее время SQL (structured query language - язык структурированных запросов) является стандартом для работы с реляционными базами данных как де-юре, в качестве стандарта ANSI, так и де-факто, поскольку его поддержку реализуют все современные серверные системы управления базами данных (DB2, Oracle, Informix, Sybase, MS SQL Server и многие другие). Реализована поддержка SQL и для настольных СУБД - Paradox, dBase, MS Access и проч. Так что роль и применимость SQL достаточно высоки. Предложения SQL просты и лаконичны, тем не менее, достижение результата исполнения таких предложений иными средствами может привести к достаточно сложному (и, безусловно, ненужному) кодированию.
О разных аспектах SQL говорить можно много, но в этой статье я хотел бы поделиться с читателями своим опытом практического использования такой его конструкции, как Union. Прошу рассматривать все изложенное с точки зрения не "как нужно делать", а "как в принципе имеется возможность делать". Надеюсь, синтаксис SQL читателю знаком.
Union позволяет объединить результаты выполнения нескольких запросов к разным таблицам в одном подмножестве данных, реализуя один запрос. Например:
select Afield1, Afield2 from A union select Bfield1, Bfield1 from B;
Непременное требование - одинаковые количество и типы полей в составляющих предложение Union запросах. С первым требованием (количество полей) не поспоришь. Второе (одинаковый тип) - можно обойти, для этого необходимо использовать преобразование типов в запросе. Если "родной" тип поля совпадает с типом, к которому это поле нужно привести, то преобразование не требуется. Если поля в запросе требуется как-то именовать, то это можно сделать только в первой select-секции:
select cast(IntegerField as char(15)) as OneField from A union select Char15Field from B;
Здесь IntegerField - поле типа Integer, Char15Field - поле типа char(15).
Итак, в одном подмножестве объединены числовые и строковые данные. "В огороде бузина, а в Киеве - дядька", не так ли? Для чего это нужно? Например, в области автоматизации делопроизводства возникает задача вывести список всех документов, положенных в данную папку. При этом каждый документ хранится в отдельной таблице данных. В каждой из этих таблиц есть поле FolderID, содержащее номер папки. Текст запроса к таблицам договоров и актов из папки №2 с конструированием текстовых полей в запросе и сортировкой документов по датам будет примерно такой:
select SignDate as DocumentDate, cast(ContractKind || ' ' || ContractNumber || ' ' || ContractSubject as char(100)) from Contracts where FolderID = 2 union select ActDate, cast('Акт № ' || cast(ActNumber as char(8)) || ' ' || ActName as char(100)) from Acts where FolderID = 2;
Очевидно, что все поля, участвующие в конкатенации - типа char, а если не так, то необходимо отдельное преобразование, как это сделано с полем ActNumber, которое имеет другой тип, допустим, Integer.
А вот еще одно применение Union. При создании приложений, работающих с базами данных, иногда может возникнуть необходимость отобразить, помимо данных из таблицы, некоторые другие значения. Допустим, мы строим окно, в котором нужно разместить две таблицы - "Виды документов" (главная) и "Список документов" (подчиненная). Всякий раз, когда мы выбираем в главной таблице вид документа, в подчиненной получаем список документов выбранного вида. Допустим теперь, что есть необходимость в подчиненной таблице получать список всех документов, а не только конкретного вида, то есть нужно в список видов документов ввести виртуальную запись "<Все документы>" и разместить ее в начале списка. SQL-запрос будет такой:
select cast(2 as Integer) as theSequence, DocumentKind from DocKind union select cast(1 as Integer) as theSequence, cast('<Все документы>' as char(40)) from OneRec order by 1, 2;
Имеется в виду, что поле DocumentKind имеет тип char(40). Благодаря использованию фиктивного поля theSequence мы выносим запись "<Все документы>" в начало списка. (Фраза "order by 1, 2" в данном случае избыточна). Единственное "НО": необходима таблица OneRec. Что это за таблица? Это любая таблица, имеющая только одну запись - естественная или специально созданная для того, чтобы выполнить такой(ие) запрос(ы). Если записей в ней будет несколько, то будет и несколько строк "<Все документы>" в результирующем подмножестве. Можно запихать полученное подмножество в компонент DBLookupComboBox (в Delphi или C++ Builder), получится форма с одной таблицей и группировочным боксом. Естественно, реакцию программы на передвижение по такому списку придется "прописывать" вручную.
Вероятно, есть и другие примеры практического применения SQL для достижения хорошего результата простыми средствами. Буду рад, если поделитесь опытом.
Юрий А. СМАНЦЕР,
georgesman@mail.ru
Комментарии
Вот такая вот история.
а через приведение типов какое-нибудь не пробовал?