$UnDef — Ключевое слово Delphi


Ключевое слово Delphi

Я изучаю Delphi, читаю книгу Марко Канту, и она супер полная. Это очень ясно, но я сомневаюсь в ключевом слове self . У меня уже есть опыт работы с ООП, и у меня есть основы этого. Мой вопрос очень прост. Можно ли сравнить ключевое слово self (Delphi) с ключевым словом this (Java)?

Когда я прочитал книгу о self используемой внутренней записи, я понял, что-то вроде self : Delphi = this : Java . Посмотрите на код, который я создал для проведения теста:

Я собираюсь отрезать большую часть кода, я просто показываю конструктор здесь:

Используя ключевое слово self здесь, я имею в виду символ записи , а не символ, переданный в методе. Правильно ли это использовать себя? Может ли это быть братом Java this ?

mirsovetov.net

Андрощук Александр, ИТ решения, советы, заметки…

Delphi — <$IFDEF>более одной директивы

Задача: Каким образом можно определить более одного условного оператора в одной проверке <$IFDEF>?
Инструментарий: Delphi
Решение:
Для того чтобы выполнить проверку нескольких условных операторов в одной проверке <$IFDEF>— нужно воспользоваться директивой .

Пример использования:

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

Delphi.FireDAC. Выполнение SQL команд

Данная статья о выполнении SQL команд в FireDAC. Основа для статьи – официальная документация.

Далее мы рассмотрим как отправлять команды через различные компоненты FireDAC.

TFDConnection

TFDConnection предлагает несколько перегруженных ExecSQL методов. Они просты для использования и полезны, когда

-Не нужно возвращать никаких результатов

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

-Нет необходимости для расширенной настройки параметров

-Нет необходимости для работы с SQL в дизайн режиме

Далее, в документации приводится такой пример

Для параметризованного запроса достаточно выполнить

Также TFDConnection предлагает методы ExecSQLScalar, отличные от ExecSQL. Как написано в документации ExecSQLScalar возвращает значение первой колонки в первом ряду первого result set

TFDQuery

FDQuery это более универсальный компонент для работы с SQL запросами. Он может и выполнять команды, не возвращающие множеств и наоборот, выполнять команды, возвращающие множества.

FDQuery можно использовать как в Runtime так и в Design режимах. В документации приводится пример как можно пользоваться компонентом в Design режиме

FDQuery в дизайн режиме

Чтобы воспользоваться FDQuery в дизайн режиме, нужно бросить его на форму. Свойство TFDQuery.Connection ,будет автоматически установлено, если на форме присутствует FDConnection. Двойной клик по компоненту и мы получим Query Editor

В этом окне можно задавать SQL команды, определять параметры и макросы, а также настраивать опции.

При нажатии на Execute выполнится запрос. Если команда возвращает множество, оно будет отображено во вкладке RecordSet, а структура множества будет отражена во вкладке Structure. Если DBMS возвращает сообщения об ошибках или предупреждения, они будут отражены во вкладке Messages.


Кнопка NextRecordSet позволяет пройтись по всем множествам, возвращаемым командой.

При тестировании DML команд, таких как UPDATE/INSERT/DELETE, если не подразумевается изменение множества из данного окна, то лучше поставить галочку AutoRollBack.

Кнопка OK для сохранения изменений в TFDQuery.

Использование параметров

Параметры в запросах это один из лучших приемов при разработке приложений БД. Главные достоинства это:

-Мы строим только одну SQL команду и используем её несколько раз с разными значениями параметров. DBMS, в свою, очередь будет строить план выполнения команды только один раз. Это снижает загрузку DBMS.

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

-Не придется заботиться о формате SQL констант, например константа даты в Access SQL.

Чтобы добавить параметр в SQL запрос, достаточно использовать следующий синтакс :

То есть, в SQL запросе просто пишем двоеточие и имя константы. После этого ассоциируем соответствующие значения, используя коллекцию параметров. Далее в документации приводится такой пример

$UnDef — Ключевое слово Delphi

Прежде чем перейдем к дальнейшему описанию языка Delphi, формально опреде-
лим несколько терминов. Во-первых, это слово «идентификатор». Идентификатор —
это строка символов, используемая для именования некоторого элемента программы.
Это может быть переменная, запись, функция, процедура или конструкция более вы-
сокого уровня, например сама программа.
Идентификатор может иметь любую длину, однако в языке Delphi только первые
его 255 символов являются значимыми (что более чем достаточно!). Идентификатор
должен начинаться с буквы или символа подчеркивания (_) и не может содержать
пробелов. После первого символа идентификатора можно использовать буквы, цифры
и символы подчеркивания. Как и в зарезервированных словах, в идентификаторах
можно использовать как строчные, так и прописные буквы (компилятор их не разли-
чает). Приведенные ниже идентификаторы означают одно и то же.

CalculateValue
calculateValue
calculatevalue
CALCULATEVALUE
Ключевые слова не могут быть идентификаторами.
Далее рассмотрим лексемы. Это минимальные значимые единицы текста в про-
грамме. Они представлены такими категориями, как специальные символы, иденти-
фикаторы, метки, числа и строковые константы.
Программа, написанная на языке Delphi, состоит из лексем и разделителей, при-
чем разделитель представляет собой пробел или комментарий. Две соседних лексемы,
если они представляют собой зарезервированное слово, идентификатор, метку или
число, должны быть отделены друг от друга одним или несколькими разделителями.
В Delphi используются следующие подмножества набора символов кода ASCII.
• Буквы английского алфавита от А до Z и от а до z.
• Цифры — арабские цифры от 0 до 9.
• Шестнадцатеричные цифры — арабские цифры от 0 до 9, буквы от А до F и бу-
квы от а до f.
• Пробелы — символ пробела ($32) и все управляющие символы кода ASCII ($0-$31),
включая символ конца строки или символ возврата каретки ($13). Это шестна-
дцатеричные числа, так как перед ними стоит символ доллара «$».
Теперь определим смысл слова «выражение». Это фрагмент языка программирова-
ния, представляющий способ вычисления некоторого значения.
И наконец, определим смысл слов «операнд» и «оператор».
Операнд — часть выражения, над которым производятся операции. Например,
в выражении, присваивающем А сумму в и с (А := В+С;), А, в, с являются операн-
дами, а над значениями, представленными идентификаторами А й в , производится
операция суммирования. Идентификатор — это строка символов, используемая для именования некоторого
элемента программы.
Лексемы — это минимальные значимые единицы текста в программе.
Выражение — это фрагмент языка программирования, представляющий способ вы-
числения некоторого значения.
Операнд — часть выражения, над которым производятся операции.
Оператор — действие, которое может быть выполнено над одним или несколькими
операндами.
Оператор — действие, которое может быть выполнено над одним или несколькими
операндами. Если обратиться к вышеприведенному примеру, то оператором является
знак плюс (+). Хотя в некоторых случаях оператором можно назвать целое выражение,
заканчивающееся точкой с запятой. Более правильно такие операторы надо называть
структурированными операторами. Например, выражение
while i:=0 to 10 do x := i ;
можно назвать оператором, так как здесь выполняется операция цикла над пере-
менной X.
Теперь можно переходить непосредственно к ключевым словам. Обычно ключевые
слова пишутся строчными буквами, но Delphi безразличен к регистру клавиатуры, по-
этому можно использовать в своей программе как строчные (нижний регистр), так
и прописные (верхний регистр) буквы. Я рекомендую использовать какой-то один
стиль написания, например, тот, к которому вы уже привыкли. Но если вы только на-
чинаете программировать, то лучше использовать общепринятые правила и писать
ключевые слова строчными буквами. В табл. 3.1 приведен перечень всех ключевых
слов с кратким комментарием.
Таблица 3.1. Ключевые слова
Ключевое слово Комментарий
and Булев оператор И
array Массив
as Используется при проверке соответствия типов, определяет объект как операнд
asm Используется для выделения ассемблерного кода
begin Начало блока
case Оператор выбора. Используется при выборе из многих вариантов
class Определяет тип «класс»
const Определяет константы, т.е. неизменяемые переменные. Однако в Delphi есть
режим, допускающий изменение констант в теле программы
constructor Специальный метод класса, необходимый для создания и инициализации
экземпляра класса (объекта)
destructor Специальный метод класса, необходимый для разрушения объекта
d i s p i n t e r f a c e Определяет тип интерфейса
div Целочисленное деление
do Определяет начало исполнимой части в операторах цикла, конструкции
t r y . . . except и в операторе w i t h
downto Определяет направление итерации в операторе f o r
else Используется в операторах выбора case, условном операторе i f и в операторе
проверки исключений t r y . . .except
end
except
e x p o r t s
f i l e
f i n a l i z a t i o n
f i n a l l y
for
f u n c t i o n
goto
i f
implementation
i n
i n h e r i t e d
i n i t i a l i z a t i o n
i n l i n e
i n t e r f a c e
i s
l a b e l
l i b r a r y
mod
n i l
not
o b j e c t
of
or
out
Обычно используется совместно с ключевым словом begin и отмечает конец
блока. Также ставится в конце описания типа, например класса или записи
Используется в операторе проверки исключений t r y . . . except
Определяет список экспортируемых процедур, функций и переменных
Устанавливает тип переменной как файл. Используется при работе с файлами
Определяет начало раздела, который в программе всегда выполняется
последним
Используется в операторе проверки исключений t r y . . . f i n a l l y
Используется в операторах цикла f o r . . . to и f o r . . .downto
Используется при объявлении функций
Переход на метку
Используется в операторах выбора i f . . . then и i f . . . then. . .else
Определяет раздел реализации, в котором находятся описания процедур,
функций, методов и коды разрабатываемой программы
После этого ключевого слова может указываться путь к необходимому модулю.
Также используется при работе с множествами
Дословно можно перевести как «унаследованный». Используется при работе
с классами, поддерживая возможности полиморфизма
Определяет раздел инициализации, который всегда располагается перед
разделом f i n a l i z a t i o n . Если раздела f i n a l i z a t i o n нет, то раздел
инициализации находится перед завершением программы. Выполняется сразу
после запуска программы, перед всеми другими операторами. Обычно
используется для инициализации переменных
Используется при работе с ассемблерным кодом. Устаревшее ключевое слово
к применению не рекомендуется
Определяет тип интерфейса. Используется при опережающем объявлении
интерфейса
Используется при проверке типов
Метка. Используется совместно с ключевым словом goto. Может быть
выражена любым идентификатором или числом от 0 до 9999
Директива-напоминание или рекомендательная директива. Используется
наравне с директивами p l a t f o rm и deprecated для напоминания об
особенностях стандартных типов, методов или модулей. Во время компиляции
вызывает появление предупреждающего сообщения
Остаток от деления целых чисел
Специальная константа, которая может быть присвоена любому указателю,
после чего считается, что указатель не ссылается ни на что
Булев оператор отрицания
Используется как альтернатива слову class. Сохранено в языке для
совместимости со старыми версиями. Не рекомендуется к использованию
Используется во многих операторах как связующее ключевое слово
Булев оператор ИЛИ
Используется при объявлении параметров процедуры, функции или метода.
Предупреждает о том, что данный параметр используется только для
выдачи значений
packed
p r o c e d u r e
program
p r o p e r t y
r a i s e
r e c o r d
r e p e a t
r e s o u r c e s t r i n g
s e a l e d
set
shl
s h r
s t r i n g
then
t h r e a d v a r
t o
t r y
type
u n i t
u n t i l
uses
var
while
w i t h
xor
Используется для более плотного размещения данных в структурированных
типах (массивы, множества, записи, файлы, классы)
Используется при объявлении процедур
Определяет имя программы, которое должно быть выражено
идентификатором
Используется при объявлении свойств
Используется при генерации исключений
Определяет тип записи
Используется в операторе цикла repeat. . . u n t i l
Определяет раздел объявления ресурсов
Используется при объявлении класса, запрещая наследование данного класса
Ключевое слово для объявления множества
Логический оператор сдвига влево
Логический оператор сдвига вправо
Используется при объявлении строковых типов
Используется в операторах i f . . . then и i f . . . t h e n . . .else
Используется при разработке многопоточных приложений
Используется в операторе f o r . . . t o
Используется в операторе проверки исключений t r y . . . f i n a l l y ,
t r y . . .except и в операторе выбора case
Определяет раздел объявления типов
Модуль. Обычно это функционально законченный фрагмент программы,
сохраняемый в файле с таким же именем
Используется в операторе repeat. . . u n t i l
Определяет раздел подключаемых модулей
Определяет раздел переменных
Используется в операторе w h i l e . . . do
Используется для определения идентификатора, который всегда записывается
с другими идентификаторами. Код получается более компактным и понятным
Булев оператор Исключающее ИЛИ
Есть еще несколько ключевых слов, о которых мы поговорим при изучении объектно-
ориентированного программирования и которые могут использоваться как директивы.
Именно эти ключевые слова выделяются жирным шрифтом в редакторе кода, хотя
кое-что может вызвать недоумение: почему, например, слово Break, которое в других
языках программирования является ключевым, в редакторе кодов не подсвечивается?
В Delphi это не ключевое слово, а процедура, и для нее существует отдельное описа-
ние в библиотеке. А процедура write не описана в библиотеках, так как ее код встро-
ен в компилятор. Сейчас мы не будем разбираться в этих тонкостях, а только отме-
тим, что все ключевые слова обычно пишутся строчными буквами, а процедуры
обычно начинаются с прописной буквы.
Написание идентификаторов также можно сделать нагляднее, если использовать слова,
отражающие назначение идентификатора, и начинать каждое слово с прописной буквы.
Например, идентификатор для счетчика символов можно написать так: SymbolCounter. Здесь слова использованы полностью, а разделение достигается за счет того, что второе
слово начинается с большой буквы. Каждый, кто будет читать программу, где использо-
ваны подобные идентификаторы, сможет понять ее без дополнительных комментариев.
Используйте это правило, и ваши программы станут понятнее и для вас самих. (К сожа-
лению, все идентификаторы должны писаться только английскими буквами, поэтому
учите английский, в настоящее время это язык общения программистов.)

Блог GunSmoker-а (переводы)

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.

четверг, 29 апреля 2010 г.

Absolute(но) для начинающих

Это перевод Absolute (for) Beginners. Блог: Te Waka o Delphi.

Сегодня я случайно предложил использование ключевого слова «absolute» в ответе на вопрос в списке рассылок NZ DUG. Я сразу забыл об этом, но потом кто-то упомянул, что он не видел, как использовали это ключевое слово, вот уже много лет. Поэтому я подумал, что, может быть, будет целесообразным довести его до более широкого внимания.

absolute — это одна из тех возможностей языка, которые, я уверен, поделят всех на два лагеря: тех, кто считают его полезным инструментом и тех, кто считает, что его нужно избегать в современном мире (вроде как goto). Лично я твёрдо состою в первом лагере.

absolute позволяет вам указать компилятору рассматривать одну или несколько переменных как существующие в одном месте в памяти, вроде вариантных записей, когда одна переменная перекрывает другую.

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

Наверное, пример сможет указать, что я имел тут ввиду.

Представьте обработчик события, который отвечает за изменения во множестве элементов управления, когда точный тип элемента управления не важен, но реакция обработчика зависит от него. Это довольно обычный шаблон, когда вы не хотите заводить множество обработчиков событий. Давайте, для примера, все редактируемые элементы управления на форме должны вводить текст только в верхнем регистре (пожалуйста, имейте ввиду, что это просто пример; не надо к нему придираться):
Итак, первым способом улучшить этот код могло бы быть:
Я не знаю, как вы, но я думаю, что в этом случае лекарство оказалось хуже болезни. Тогда давайте теперь посмотрим, как «absolute» сделает эту попытку улучшения намного более успешной:
Второй вариант настолько же типобезопасен, как и первый — проверка типов производится в тех же местах. Отличие в том, что приведение типов теперь выполняется при самом объявлении переменных, поэтому вам не нужно его делать руками в коде.

Ключевое слово absolute говорит компилятору, чтобы он трактовал переменные edit, memo и combo просто как разные виды одной и той же переменной control.

Но тут есть что-то большее — лично для меня, это также является чётким указанием на то, как эти переменные будут использоваться в теле процедуры (причём, прямо в объявлении). В то время как в первом примере этого не видно, без изучения кода метода.

Мне нравится код, который объясняет сам себя — это означает, что мне не нужно писать много в комментариях!

Теперь, фактически, мы можем сделать всё прозрачнее для понимания и более компактным, поскольку, равно как и «перекрывать» переменные, absolute может «перекрывать» и параметры процедуры, так что мы можем избавится от отвлекающей переменной control:
Как я уже сказал, это не очень красивый и очень надуманный пример, но я хотел использовать что-то простое для представления идеи, прежде чем немного усложнять вещи.

Изменение сущности шизофреничных типов


Если вам когда-либо приходилось работать с ресурсами в своих приложениях — иконками, растровыми изображениями и т.д. — вы, несомненно, знакомы с MAKEINTRESOURCE(). Эта маленькая функция преобразует 16-битный идентификатор ресурса в значение, которое может быть использовано вместо указателя на название ресурса в некоторых функциях.

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

Возможно, это неудобно, но absolute позволяет легко и, опять же, прозрачно указать цель вашего кода, без необходимости явно выполнять код для выполнения преобразования:
Заметьте: проверка старших 16-ти битов параметра может быть записана в следующей эквивалентной форме:
Заметьте, как эта техника обеспечивает безопасность типов для передаваемого параметра (идентификатор ресурса, передаваемого как PChar) без привлечения преобразований типов в коде процедуры. Для сравнения: вот обычный подход к этой проблеме без использования absolute: Ну, может разница и не слишком велика (хотя на этот раз это пример из реальной жизни), но мы всё же объявили переменную и использовали преобразование типов без особой на то причины (в смысле назначения функции), что просто позволило нам работать с переданным значением другим способом. Я думаю, что у вас может быть желание убрать переменную и просто добавить ещё больше преобразований типов: Что несколько короче и экономнее, но. видимо, дело в том, что некоторые люди просто любят такой код, с множеством повторений и нагромождением скобок. Лично я — нет.

К концу начала

Как я уже сказал вначале, всё это началось, когда я ответил на вопрос в списке рассылки NZ DUG. Вопрос был о том, как воспользоваться преимуществами языковой конструкции for in, представленной в Delphi 2007. Тот случай касался попытки использование for in для перебора содержания экземпляра класса-наследника от TObjectList — то есть свой энумератор для класса ещё не был создан (и имейте в виду, что дженерики не были доступны, потому что Delphi версия в вопросе не имела их поддержки в Win32). Человек в вопросе спрашивал почему следующий код даёт ошибки компиляции и как их избежать (я немного изменил и упростил код): Поскольку TAddressFormatList наследуется от TObjectList, то этот код не компилируется из-за ошибок несовместимости типов, поскольку перечислитель для TObjectList возвращает Pointer (прим. пер.: наследуется от TList), а не TAddressFormat. Вопрос был задан человеком, который видел демонстрацию этой возможности языка, где цикл for in был написан для обработки содержимого экземпляра TStringList, но он при этом не понял механизм, который делает это возможным, не понял, что для этого нужна инфраструктура, которую кто-то должен написать, чтобы for in работал (которая, конечно же, в случае TStringList и некоторых других типов, уже написана авторами RTL/VCL). Вы, конечно, можете делать так, как требует от вас TObjectList: делать перечисление, используя переменную типа Pointer, приводя её тип к нужному объекту в теле цикла: Но если вам нужно в теле цикла обращаться к addrFormat несколько раз, то это весьма хлопотно. Поэтому вы захотите ввести другую, строго типизированную переменную, так что вы сделаете преобразование типа только один раз при присвоении и в дальнейшем будете использовать только вторую переменную: Но мы уже видели, как absolute может помочь с упрощением таких ситуаций, делая отношение между двумя переменными чётко выраженными при их объявлении: Вот и всё. Как я уже сказал, ожидайте, что к этой заметке будут комментарии, указывающие на опасности использования этой техники, но я считаю, что эти опасности не превышают (а иногда и меньше) тех, которые вы получаете, используя все эти безусловные приведения типов. К преимуществам absolute также относится уменьшение «шума» в коде, а также оно делает назначение переменных более ясным, без необходимости чтения кода. absolute, хотя бы, может стать еще одним инструментом в вашем ящике с инструментами. Или, оно может быть инструментом, о котором вы даже не знали, что он у вас есть.

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

Примечание переводчика: в случае с for in использование absolute является лишь обходным решением. Если вам нужно делать for in во многих местах, то правильным решением будет написание энумератора для класса, а не использовать каждый раз доп. переменную.

$UnDef — Ключевое слово Delphi

В предыдущих главах, посвященных работе с СУБД, мы рассмотрели базовые аспекты работы с данными с использованием средств VCL. Вместе с тем, при работе с СУБД очень часто возникает необходимость в выполнении более сложных операций. Например, это может быть сложная выборка данных по таким условиям, которые проблематично или вовсе невозможно определить при помощи фильтра — например, из нескольких таблиц одновременно. Для таких целей используют специальный язык, который «понимают» СУБД — язык SQL, а для создания обращений к СУБД на этом языке в VCL имеется специальный компонент — Query.

Язык SQL

Все современные клиент-серверные СУБД имеют одну общую черту — работа с ними строится на языке SQL — Structured Query Language (структурированный язык запросов). Кроме того, чтобы не лишать разработчиков БД на Delphi возможности работать при помощи SQL с обычными файловыми СУБД типа dBase и Paradox, BDE предоставляет возможность прозрачного использования SQL и для них, используя свои внутренние механизмы.

Стандарт языка SQL определяется ANSI (Американским национальным институтом стандартов). Вместе с тем, каждый производитель СУБД считает своим долгом дополнить язык собственными расширениями которые, как они считают, будут весьма полезны. Иногда они несколько нарушают стандарт языка, хотя хорошие идеи имеют тенденцию развиваться и вскоре становиться стандартами де-факто сами по себе в силу полезности своих качеств. Здесь мы будем рассматривать SQL на основе наиболее полно поддерживаемых всеми СУБД вариантами ANSI-89 и ANSI-92.

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

  • Языка манипулирования данными (Data Manipulation Language, DML);
  • Языка определения данных (Data Definition Language, DDL);
  • Языка управления данными (Data Control Language, DCL).

Все эти языки являются составной частью языка SQL. Фактически, каждый из них содержит набор команд SQL, предназначенных для своей области. В процессе работы с БД, пожалуй, чаще всего используется язык манипулирования данными, состоящий из 4 основных команд — SELECT, INSERT, UPADTE и DELETE, при помощи которых производится, соответственно, выборка, вставка, обновление и удаление данных.

Язык определения данных служит для создания и изменения структуры БД — таблиц, индексов и т.д. Он состоит из 3 групп команд — CREATE, ALTER и DROP (создание, изменение и удаление, соответственно), каждая из которых может манипулировать с одним из 6 объектов — базой данных, таблицей, виртуальной таблицей, индексом, триггером или хранимой процедурой. Таким образом, например, для команды CREATE мы получаем следующие 6 вариантов:

  • CREATE DATABASE — создать базу данных;
  • CREATE TABLE — создать таблицу;
  • CREATE VIEW — создать виртуальную таблицу;
  • CREATE INDEX — создать индекс;
  • CREATE TRIGGER — создать триггер;
  • CREATE PROCEDURE — создать хранимую процедуру.

Впрочем, триггеры и, в особенности, хранимые процедуры относятся исключительно к компетенции промышленных СУБД, которые мы в этой книге рассматривать не будем. То же самое относится и к языку управления данными (иногда его еще называют языком управления доступом) — он состоит из 2 основных команд — GRANT (дать права) и REVOKE (забрать права).

ПРИМЕЧАНИЕ
На самом деле, для современных клиент-серверных СУБД существует ряд дополнительных команд SQL, позволяющих, например, определять собственные функции для обработки данных (CREATE FUNCTION), создавать резервные копии таблиц, преобразовывать типы таблиц и т.д. Далеко не все они входят в состав стандарта языка SQL, поэтому информацию по ним следует брать из поставляемой вместе с конкретной СУБД документации.

Мы не будем рассматривать точный синтаксис всех команд SQL, поскольку это потребовало бы объема, соизмеримого с целой книгой. Вместо этого мы рассмотрим основные команды на примерах, что намного более важно для понимания SQL, чем точный синтаксис, который, при необходимости, всегда можно посмотреть в документации на используемую СУБД. А начнем с рассмотрения команд языка манипулирования данными.

Команда SELECT

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

SELECT выражения_для_выборки FROM таблицы [параметры выборки]

В качестве выражения для выборки обычно перечисляют поля таблиц. Если требуется вывести все поля, то используют символ «*»:

SELECT BILL_ID, BILL_SUMM FROM BILL SELECT * FROM BILL

В первом случае будут выведены поля BILL_ID и BILL_SUMM из таблицы BILL, а во втором — все поля из этой же таблицы. В этом нетрудно убедиться, воспользовавшись утилитой SQL Explorer для написания запроса. Для этого запустите SQL Explorer, откройте нужную БД, например, DATA1 (или предварительно создайте новый алиас для более полной версии БД, созданной для примера DBApp), и в правой части окна щелкните по закладке Enter SQL, после чего введите нужный код и нажмите на кнопку Execute Query. Результат выполнения запроса незамедлительно будет выведен в таблицу внизу (рис. 21.1).


Рис. 21.1. Приложение SQL Explorer

СОВЕТ
Если у вас установлена версия Delphi, не имеющая этого инструмента, то для экспериментов с командой SELECT вы можете использовать приложение SQLELE, которое можно найти в каталоге Tools\SQLE_LE.

При выборе можно сразу же произвести упорядочивание записей, причем SQL позволяет делать эту операцию по любому полю таблицы, а не только по индексированному. Например, чтобы вывести все счета в порядке возрастания суммы, достаточно написать следующее выражение:

SELECT * FROM bill ORDER BY BILL_SUMM

Если бы требовалось выполнить сортировку не по возрастанию, а по убыванию, то в конце выражения следовало бы добавить ключевое слово DESC:

SELECT * FROM bill ORDER BY BILL_SUMM DESC

Выборка записей из таблицы производится при помощи условия, определяемого после ключевого слова WHERE. Например, если надо выбрать только те счета, которые были после 15 декабря, следует написать следующий SQL-запрос (формат написания даты в данном случае зависит от системных установок):

SELECT * FROM bill WHERE BILL_DATE>’15.12.2005′

Если при этом так же требуется еще и выполнить сортировку, то в конец выражения добавляют определение ORDER BY:

SELECT * FROM bill WHERE BILL_DATE>’15.12.2005′ ORDER BY BILL_SUMM

В случае, когда условий больше одного, то их объединяют при помощи логических AND или OR:

SELECT * FROM bill WHERE BILL_DATE>’15.12.2005′ AND BILL_SUMM>100 SELECT * FROM bill WHERE BILL_DATE>’15.12.2005′ OR BILL_SUMM>300

Все эти примеры иллюстрируют общую форму команды SELECT в языке SQL (для одной таблицы): SELECT (выбрать) указанные поля FROM (из) указанной таблицы WHERE (где) заданные условия истинны.

Немного более сложным вариантом является выборка данных из 2 и более таблиц сразу. Например, для наглядности не помешало бы вывести в виде таблице список счетов, содержащий не внутренние номера клиентов, а их имена. В этом случае придется задействовать 2 таблицы — bill и customer, причем связующим полем будет номер клиента, т.е. поле BILL_CUST в таблице счетов и поле CUST_ID в таблице клиентов. В результате, мы получаем следующее выражение:

SELECT CUST_NAME, BILL_SUMM FROM bill, customer WHERE CUST_ >

Опять-таки, к данному условию, связывающему поля таблиц, можно добавить дополнительные, скажем, ограничивающие даты или суммы, равно как и определить упорядочивание вывода. Например, можно добавить условие, что сумма счета должна быть свыше 250, а так же задать упорядочивание по имени клиента (рис. 21.2).

Рис. 21.2. Результат выполнения SQL-запроса по 2 таблицам

Следует учитывать, что подобное связывание таблиц — путем указания условия в конструкции WHERE, является устаревшим и применяется лишь тогда, когда требуется добиться совместимости с SQL89. Более новый стандарт, SQL92, рекомендует использовать для связывания таблиц ключевое слово JOIN. Таким образом, предыдущий пример можно переписать следующим образом:

SELECT CUST_NAME, BILL_SUMM FROM customer JOIN bill ON CUST_ >

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

SELECT CUST_NAME, BILL_SUMM FROM customer JOIN bill ON CUST_ >250; SELECT CUST_NAME, BILL_SUMM FROM customer JOIN bill ON CUST_ >250;

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

SELECT EXTRA_USER_DATA_INFO AS a, EXTRA_USER_DATA_NAME as b FROM USER_TABLE WHERE a > 10 AND a

Следует отметить, что при выводе полей таблиц с помощью SQL можно производить некоторые вычисления. В этих случаях использование локальных имен бывает просто необходимым. Допустим, в счетах у нас хранятся суммы без НДС. В таком случае мы можем ввести сразу 2 столбца с суммами — с той, что хранится таблице и в виде вычисленного значения.

SELECT BILL_DATE, BILL_SUMM, BILL_SUMM * 1.18 as WITH_TAX FROM BILL ORDER BY WITH_TAX

В таком случае названием столбца с вычисленным значением будет «WITH_TAX» (см. рис. 21.3).

Рис. 21.3. SQL-запрос с вычислениями

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

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


SELECT COUNT(*) FROM bill

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

SELECT COUNT(*) FROM bill WHERE BILL_DATE

Помимо COUNT, в стандартном SQL предусмотрено еще 4 агрегирующих функции — для вычисления суммы (SUM), максимального (SUM) и минимального (MIN) значений столбцов, а так же среднего арифметического (AVG). Результатом выполнения запроса по всем этим функциям для таблицы счетов будет таблица, состоящая из 4 столбцов, в каждом из которых будет находиться результат вычислений (рис. 21.4).

Рис. 21.4. Результат выполнения запроса с агрегирующими функциями

Что касается заданий ограничений на обрабатываемые данные, ровно, как и на выводимые при помощи «обычного» запроса строки, то помимо реляционных операторов сравнения — больше (>), меньше ( ), больше или равно (>=), меньше или равно ( SELECT BILL_SUMM FROM bill WHERE BILL_SUMM BETWEEN 100 and 200; /* Вывести счета с суммами от 100 до 200 */ SELECT BILL_SUMM, BILL_CUST FROM bill WHERE BILL_SUMM IN (100,150,200) /* Вывести счета с суммами, равными 100, 150 и 200 */ SELECT CUST_NAME FROM customer WHERE CUST_NAME LIKE ‘OOO «Gamma%’ /* Вывести клиентов, имя которых начнается с «ООО «Gamma» */ SELECT CUST_NAME FROM customer WHERE CUST_NAME LIKE ‘OOO «_____»‘ /* Вывести клиентов, имя которых начнается с «ООО «» и имеет 5 символов в кавычках */ SELECT CUST_NAME FROM customer WHERE CUST_NAME LIKE ‘_%’ /* Вывести клиентов, имя которых состоит хотя бы из 1 символа */

Что касается порядка вывода результатов, то помимо сортировки, еще одним способом повлиять на вывод результатов запроса является использование GROUP BY. Эта конструкция определяет порядок группировки полей при выводе. При этом следует учитывать, что должны быть указаны все поля, перечисленные в запросе:

SELECT BILL_SUMM, BILL_DATE FROM bill GROUP BY BILL_DATE, BILL_SUMM

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

SELECT BILL_CUST, SUM(BILL_SUMM) FROM BILL GROUP BY BILL_CUST

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

SELECT CUST_NAME, SUM(BILL_SUMM) as TOTAL FROM BILL JOIN CUSTOMER ON CUST_ >

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

Рис. 21.5. Запрос с группировкой, агрегацией и упорядочиванием по вычисляемому полю

Все, что было на данный момент рассказано про команду SELECT, на самом деле, является лишь верхушкой айсберга ее возможностей. В то же время, даже такое беглое знакомство дает понять, что без знания SQL написать достаточно объемное приложение для БД крайне затруднительно. В то же время, каждая СУБД, как уже было отмечено, имеет свои тонкости в реализации языка, и команда SELECT, по причине своих огромных возможностей, является одним из наиболее частых претендентов на «модернизацию». По этой причине для дальнейшего изучения как команды SELECT, так и прочих возможностей SQL, следует использовать документацию к используемой СУБД.

Некоторые другие команды SQL

Помимо уже рассмотренных запросов на выборку данных при помощи команды SELECT, рассмотрим некоторые другие команды языка манипулирования данными. В частности, это команды INSERT (вставить), UPDATE (обновить) и DELETE (удалить).

Начнем со вставки — команды INSERT, которая добавляет запись с указанными значениями полей в конец таблицы. В упрощенной форме основной синтаксис этой команды можно представить следующим образом:

INSERT INTO таблица [(поля)] VALUES (значения)

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

INSERT INTO regions VALUES (77,’Москва’)

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

INSERT INTO bill (BILL_CUST, BILL_SUMM, BILL_DATE) VALUES (1, 100, ‘01.01.2006’)

В принципе, то, что выполняется при помощи команды SQL INSERT, похоже на то, что делает метод InsertRecord. Однако SQL предоставляет дополнительные возможности, в частности, вставку целой группы записей:

INSERT INTO bill (BILL_CUST, BILL_SUMM, BILL_DATE) VALUES (1, 100, ‘01.01.2006’), (1,200,’01.02.2006′), (1,150,’01.03.2006′)

Еще более широкие возможности открываются при помощи комбинирования команды INSERT с командой SELECT, причем вставку можно производить как в ту же самую таблицу, так и в любую другую. Например, если нам надо в некую таблицу bill2 внести все записи из таблицы bill, имеющие суммы свыше 300, мы можем написать следующий запрос

INSERT INTO bill2 SELECT * FROM bill WHERE BILL_SUMM>300

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


INSERT INTO bill2 (B2_SUMM) SELECT BILL_SUMM FROM bill WHERE BILL_SUMM>300

ПРИМЕЧАНИЕ
При помощи INSERT-SELECT можно очень просто копировать данные из одной таблицы в другую. В то же время, если надо просто вставить данные из подготовленного текстового файла, то ряд СУБД предлагает более удобные и быстрые команды, например, LOAD DATA INFILE в MySQL.

Другая команда SQL, применяемая для модификации данных — это UPDATE. Она позволяет изменять значения в столбцах данных таблицы. В общих чертах ее синтаксис можно представить следующим образом:

UPDATE таблица SET имя_поля = значение [WHERE условие]

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

UPDATE bill SET BILL_DATE = ‘01.01.2006’

Можно так же указывать не абсолютные, а вычисляемые значения:

UPDATE bill SET BILL_SUMM = BILL_SUMM * 2

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

UPDATE bill SET BILL_SUMM = BILL_SUMM + 10 WHERE BILL_DATE

Наконец, когда требуется изменить лишь какую-то определенную запись, что в качестве условия достаточно указать уникальное поле. Например, для таблицы счетов это поле BILL_ID, по которому можно однозначно идентифицировать какую-либо запись в таблице:

UPDATE bill SET BILL_SUMM = 1000 WHERE BILL_ >

ВНИМАНИЕ
Следует учитывать, что если предусмотренные в VCL методы для работы с БД, как правило, влияют лишь на одну запись (т.е. на ту, что выделена курсором), то в SQL изменению подвержены все записи указанной таблицы.

Наконец, остается рассмотреть последнее часто применяемое действие — удаление записей из таблицы. В SQL для этих целей используют команду DELETE, определенную следующим образом:

DELETE FROM таблица [WHERE условие]

Подобно команде UPDATE, если не указано условие, то удалению будут подвержены все данные в таблице. Например, чтобы удалить все записи в таблице счетов, достаточно написать:

DELETE FROM bill

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

DELETE FROM bill WHERE BILL_DATE

Таким образом, на текущий момент мы рассмотрели все основные команды SQL, относящиеся к языку манипулирования данными — SELECT, INSERT, UPDATE и DELETE. Во многих СУБД помимо этих команд имеется ряд дополнительных, например, TRUNCATE (аналог DELETE для всей таблицы) или REPLACE (вариация на тему вставки). Поэтому остается еще раз посоветовать ознакомиться с документацией на используемую СУБД.

То же самое касается и языка определения данных, представленного вариациями команд CREATE, DROP и ALTER: у каждой СУБД имеется собственный набор типов данных и свои тонкости работы с таблицами, индексами и т.д. В принципе, работая с «родными» для Delphi типами СУБД, вполне можно довольствоваться теми возможностями, что предоставляет утилита Database Desktop. Многие другие СУБД так же располагают собственными средствами, упрощающими процесс создания таблиц, в качестве примера можно привести широко распространенную утилиту phpMyAdmin, обеспечивающую управление СУБД MySQL через веб-интерфейс.

Компонент запроса Query

До текущего момента мы рассматривали язык SQL как таковой, оставляя вопросы его взаимодействия с Delphi. Переходя к практическим вопросам использования этого языка в приложениях, рассмотрим компонент Query. Компонент Query представляет собой компонент набора данных, записи которого формируются в результате выполнения SQL-запроса. При этом текст запроса также содержится в этом компоненте в виде свойства SQL типа TStrings.

В целом, с точки зрения использования в приложении, компонент Query похож на другой компонент BDE — Table. Подобно Table, он так же может выступать в качестве источника данных. Однако благодаря тому, что практически все параметры, относящиеся к выборке данных, определяются в тексте запроса, то среди свойств этого компонента, связывающих его с БД, имеется лишь DatabaseName.

Еще одним важным отличием компонента Query от Table является отсутствие у Query свойства ReadOnly. Дело в том, что компонент Query по своей сути обычно предоставляет данные, доступные только для чтения, т.е. связь получается односторонняя. Этот факт следует учитывать, т.к. в ряде случаев при установке взаимодействий между компонентами бывает необходимым ссылаться на источники данных, поддерживающих непосредственную правку. В то же время, если выполняется ряд определенных условий, в частности, запрос обращается только к одной таблице, сама таблица поддерживает запись, а свойство RecuestLive компонента Query установлено в истину, то к такому запросу можно будет обращаться точно так же, как к таблице.

В то же время, подобные ограничение вовсе не говорит о том, что при помощи Query сложно изменять данные — просто для этих целей понадобится создавать соответствующий SQL-запрос (например, с командой UPDATE), а не пытаться использовать свойства объектов Filed. Кроме того, к Query неприменимы такие методы, как FindFirst, FindLast и т.д.

В качестве примера простейшего приложения, использующего этот компонент, рассмотрим уже упоминавшийся SQL Explorer LE. В каталоге Tools\SQLE_LE, помимо самого приложения, находится его исходный код. Не вдаваясь в подробности реализации, отметим лишь, что в приложении задействовано 3 компонента, связанных с доступом к данным — это Database, Query и DataSource. Еще один невизуальный компонент — диалог открытия файла используется для выбора каталога с БД. Весь написанный код этого приложения приведен в листинге 21.1.

Листинг 21.1. Исходный код SQLE LE


procedure TMainFrm.OpenBtnClick(Sender: TObject); begin if not OpenDlg.Execute then exit; MainDB.Connected:=false; MainDB.Params.Clear; MainDB.Params.Add(‘path=’+ExtractFilePath(OpenDlg.FileName)); MainDB.Connected:=true; RunBtn.Enabled:=true; end; procedure TMainFrm.RunBtnClick(Sender: TObject); begin Query1.Close; Query1.SQL.Text:=Memo1.Text; if pos(‘select’,lowercase(Memo1.Text))=0 then Query1.ExecSQL else Query1.Open; end;

Здесь кнопка открытия БД устанавливает путь к базе данных (подразумевается, что используется СУБД Paradox), после чего делает соединение активным, а кнопку выполнения запроса — доступной. Код для кнопки выполнения запроса нуждается в некотором пояснении: дело в том, что у компонента Query имеется 2 способа выполнения запроса — при помощи методов Open и ExecSQL. Принципиальная разница между ними состоит в том, что метод Open используется для запросов, производящих выборку данных (т.е. SELECT), в остальных же случаях предпочтительнее использовать метод ExecSQL.

Особенности работы с запросами

При рассмотрении компонента Query мы уже отметили ряд различий в подходах к использованию этого компонента по сравнению с Table. Однако этим различия в приложениях, основанных на SQL, не ограничиваются. Основным преимуществом запросов на SQL является то, что они позволяют минимизировать объем данных, которыми обмениваются СУБД и приложение. Например, когда из таблицы требуется отобрать какую-то часть записей, то в случае использования фильтрации или иных «обычных» методов, приложение запрашивает у СУБД всю таблицу. В том же случае, когда используется запрос, приложение получает лишь то, что ему требуется. Это существенным образом сказывается на быстродействии, особенно если обрабатываются большие объемы данных и при передаче информации по сети.

Для иллюстрации возможностей SQL и компонента Query модифицируем приложение «База 1.0» таким образом, чтобы оно использовало данные технологии. В частности, SQL может нам понадобиться для таких вещей, как вывод счетов для клиента, подсчет суммы счетов, внесение новых счетов и клиентов или редактирование имеющихся.

При разработке нового варианта приложения заменять без оглядки все компоненты Table на Query не представляется рациональным. Например, CustTbl, представляющий таблицу клиентов, используется исключительно для вывода полного списка клиентов и для манипуляций над ним же. В то же время, компонент BillTbl было бы рационально заменить на компонент-запрос, поскольку по счетам будет производиться выборка. Кроме того, если рассматривать приближенный к практике случай, то таблица счетов со временем может стать весьма и весьма объемной, что в случае работы по сети может сказаться на производительности. Поэтому заменим этот компонент запросом Query и назовем его BillQry. При этом свойство DatabaseName у него так же будет MainDB. Кроме того, поскольку этот запрос у нас используется в качестве источника данных для таблицы DBGrid, в которую мы позволяем вносить правку, то свойство RecuestLive следует установить в истину. С учетом того, что в данном запросе используется только одна таблица (bill), это позволит нам обращаться с ним так же легко, как с обычной таблицей. После этого останется изменить в BillDS значение свойства DataSet — вместо ссылки на отсутствующий теперь компонент BillTbl укажем для него BillQry.

Наконец, нам понадобится еще один компонент Query — для выполнения вспомогательных задач — например, для добавления новых записей в таблицы. Назовем его StdQry и установим свойство DatabaseName в MainDB. Поскольку этот запрос не будет непосредственно выводить информацию, то добавляеть ему в пару компонент DataSource не требуется.

Теперь приступим к изменению в программном коде. Начнем с функции вывода баланса — обработчика события Click для кнопки BalanceBtn. Как раз в этом случае мы задействуем оба запроса — один для того, чтобы отобразить все нужные счета (BillQry), а другой — для того, чтобы вычислить сумму (StdQry). Вариант этой функции с использованием таблиц был приведен в листинге 20.4, теперь же мы рассмотрим новый вариант, с использованием SQL-запросов (листинг 21.2).

Листинг 21.2. Вывод счетов и вычисление суммы с использованием SQL

procedure TMainFrm.BalanceBtnClick(Sender: TObject); begin Data.BillQry.Close; Data.BillQry.SQL.Text:=’SELECT * FROM bill WHERE BILL_CUST=’ +Data.CustTbl.FieldByName(‘CUST_ ).AsString; BillsFrm.ShowModal; end;

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

Теперь перепишем код для кнопок «Новый счет» и «Новый клиент». В данном случае, поскольку производить выборки не требуется, нам будет достаточно только одного запроса — StdQry, как это показано в листинге 21.3.

Листинг 21.3. Создание новых записей в таблицах bill и customer при помощи SQL

procedure TMainFrm.NewBillBtnClick(Sender: TObject); begin with BillFrm do begin Caption:=’Счет для ‘+Data.CustTbl.FieldByName(‘CUST_NAME’).AsString; ShowModal; if ModalResult<>mrOk then exit; Data.StdQry.Close; Data.StdQry.SQL.Text:=’INSERT INTO bill(BILL_CUST, BILL_SUMM, BILL_DATE)’ +’ VALUES (‘+Data.CustTbl.FieldByName(‘CUST_ID’).AsString+’, ‘+ SummEd.Text+’, »’+DateToStr(DatePick.Date)+»’)’; Data.StdQry.ExecSQL; end; end; procedure TMainFrm.NewCustBtnClick(Sender: TObject); begin with CustFrm do begin ShowModal; if ModalResult<>mrOk then exit; Data.StdQry.Close; Data.StdQry.SQL.Text:=’INSERT INTO customer (CUST_NAME, CUST_ADDRESS) ‘ +’VALUES (»’+NameEd.Text+»’, »’+AddrEd.Text+»’)’; Data.StdQry.ExecSQL; Data.CustTbl.Refresh; end; end;

Здесь следует отметить следующий момент: после выполнения запроса на добавление клиента для компонента, представляющего таблицу клиентов (CustTbl), вызывается метод Refresh. Если этого не сделать, то, хотя данные и будут добавлены в таблицу физически, обновления таблицы на экране не произойдет. В то же время для добавления счетов подобной операции делать не требуется, поскольку для того, чтобы просмотреть список счетов, пользователю в любом случае придется нажимать на кнопку «Баланс», процедура обработки которой будет всякий раз составлять и вызывать новый запрос.

Что касается изменения учетной записи клиента, то в данном случае, в принципе, можно ничего не делать, поскольку мы оставили таблицу. С другой стороны, вполне можно использовать для этих целей язык SQL и компонент запроса StdQry. Для этого достаточно заменить последние 4 строчки кода в обработчике двойного щелчка мышью у компонента CustGrd:

//старый вариант Data.CustTbl.Edit; Data.CustTbl.FieldByName(‘CUST_NAME’).AsString:=NameEd.Text; Data.CustTbl.FieldByName(‘CUST_ADDRESS’).AsString:=AddrEd.Text; Data.CustTbl.Post; //новый вариант Data.StdQry.Close; Data.StdQry.SQL.Text:=’UPDATE customer SET CUST_NAME=»’+NameEd.Text +»’, CUST_ADDRESS=»’+AddrEd.Text+»»; Data.StdQry.ExecSQL;

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

С полным кодом переработанного приложения можно ознакомиться в каталоге Demo\Part4\DBAppSQL.

Применение перечисляемых типов Delphi.

Конечно этот пост не вызовет особого интереса у «акул шоубиза Delphi-кодинга», но блог ведь читают и те кто только пробуют разобраться в Delphi и создать что-то свое. Поэтому, думаю, что для таких людей пост «Применение перечисляемых типов Delphi» поможет сделать их приложения более…профессиональными что-ли :).
Не так давно, вдохновленный переводами Александра Божко статей о новом RTTI Delphi 2010 я решил засунуть свою лень поглубже..в портмоне и начать наконец-то разбираться с тем, что такое RTTI, как использовать и т.д. Вообще тема RTTI достаточно обширна и интересна, но сегодня речь пойдет о применении возможностей RunTime Type Information при работе с перечисляемым типом данных. А чтобы не останавливаться на голой теории, рассмотрим применим наши новые знания на практическом примере.

Итак, что такое RTTI.
Runtime Type Information или сокрашенно RTTI (информация о типах времени исполнения) —это данные, генерируемые компилятором Delphi об объектах программы. RTTI представляет собой возможность языка, обеспечивающее приложение информацией об объектах (имя, размер экземпляра, указатели на класс-предок, имя класса и т. д.) и о простых типах во время работы программы. Delphi, например, использует RTTI для доступа к значениям свойств компонент, сохраняемых и считываемых из dfm-файлов и отображения их в Object Inspector.
Основополагающие определения типов, основные функции и процедуры для работы с runtime информацией находятся в модуле TypInfo. Этот модуль содержит две фундаментальные структуры для работы с RTTI — TTypeInfo и TTypeData (типы указателей на них — PTypeInfo и PTypeData соответственно). Попробуем использовать их на конкретном примере.
Допустим, нам необходимо сформировать строку-запрос к тому же Google, чтобы получить доступ к API. Что мы можем почерпнуть из документации по ClientLogin?
1. Это то, что такие параметры как AccountType и Service могут принимать вполне конкретные значения, которые можно представить в виде двух перечисляемых типов данных:

2. Ответ сервера также может содержать ограниченное количество значений (кодов) ошибок и 1 значение («Ok») при успешной аутентификации. Следовательно получим ещё один тип данных:

Теперь, когда все перечисляемые типы данных определены, возникает простой вопрос «Что дальше?». Как бы мы поступили, например, при формировании строки запроса, если б абсолютно ничего не знали про RTTI и модуль TypInfo в частности?
Как минимум, мы бы организовали, что-то наподобие этого:

Примерно то же самое и в отношении TServices. Будет ли этот код работать? Конечно будет…куда он нафиг денется. Но ведь можно сделать и по другому.
В модуле TypInfo определен следующий метод:

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

Мы получим сообщение, содержащее строку «HOSTED». Теперь применив этот метод, мы можем сократить код представленный выше всего до одной строки:

Или, если Вы используете какой-то шаблон для формирования строки, то так:

DelphiComponent.ru — бесплатно видеоуроки по Delphi, статьи, исходники


Обработка исключений в Delphi

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

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

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

Генерация исключения означает всего лишь то. что приложение создало объект исключения и максимально подробно описало ошибку.

Если мы не обрабатываем исключение (то есть не приготовлен специальный код для перехвата исключения), приложение само сделает это автоматически. Обычно приложение обрабатывает исключение, выводя на экран монитора окно с сообщением о возникшей ошибке. Например, если вы передадите функции StrToInt строку, содержащую символы, которые не могут быть преобразованы в числовое значение, или вообще пустую строку, то функция сгенерирует исключение (рис. 13.1).

Рис. 13.1. Исключение, обработанное приложением

Чтобы обработать исключение, сгенерированное функцией StrToInt, мы должны поместить вызов функции StrToInt в защищенный блок кода. Защищенным является блок кода, который может реагировать на некоторое исключение. В Delphiзащищенный блок выглядит следующим образом:

Delphi «default» ключевое слово с типами записей в старых версиях Delphi

У меня есть этот код в библиотеке Delphi Detours, который я пытаюсь выполнить в порт:

Что делает ключевое слово » default «? I предположим что-то вроде:

Правильно ли мое предположение?

Default() — недокументированная внутренняя функция, введенная для поддержки дженериков. Дизайн дженериков Delphi был в большой степени вдохновлен генераторами .net, и вам может пригодиться чтение аналитической документации для .net: https://msdn.microsoft.com/en-GB/library/xwth0h0d.aspx p >

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

Если вы хотите воспроизвести поведение Default() , выполните следующие действия:

Вызов Finalize необходим в случае управления типом. То есть, если тип управляется или содержит какие-либо элементы, которые управляются. Управляемые типы включают строки, динамические массивы, интерфейсы, варианты, анонимные методы и т.д.

Если тип не содержит управляемые типы, вызов Finalize может быть опущен. Это не помешает включить его, потому что компилятор устранит его, если не понадобится. Если вы можете быть на 100% уверены, что никаким управляемым типам не присвоено значение, вы также можете опустить этот вызов.

Инициализация по умолчанию означает следующее:

  • Ноль для числовых типов.
  • Значение с порядковым нулем для перечисленных типов.
  • False для булевых типов.
  • #0 для типов символов.
  • Пустая строка для строк.
  • Пустой вариант для Variant .
  • nil для классов, динамических массивов, интерфейсов и анонимных методов.

DelphiComponent.ru — бесплатно видеоуроки по Delphi, статьи, исходники

Обработка исключений в Delphi

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

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

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

Генерация исключения означает всего лишь то. что приложение создало объект исключения и максимально подробно описало ошибку.

Если мы не обрабатываем исключение (то есть не приготовлен специальный код для перехвата исключения), приложение само сделает это автоматически. Обычно приложение обрабатывает исключение, выводя на экран монитора окно с сообщением о возникшей ошибке. Например, если вы передадите функции StrToInt строку, содержащую символы, которые не могут быть преобразованы в числовое значение, или вообще пустую строку, то функция сгенерирует исключение (рис. 13.1).

Рис. 13.1. Исключение, обработанное приложением

Чтобы обработать исключение, сгенерированное функцией StrToInt, мы должны поместить вызов функции StrToInt в защищенный блок кода. Защищенным является блок кода, который может реагировать на некоторое исключение. В Delphiзащищенный блок выглядит следующим образом:

Илон Маск рекомендует:  Что такое код asp passwordexpireprenotifydays
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL