Форум программистов, компьютерный форум, киберфорум
Наши страницы
krapotkin
Войти
Регистрация
Восстановить пароль
Блог. Двадцать пять лет Делфи-практики

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

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

Начав с Делфи-2 двадцать пять лет назад, я прошел все версии, испробовал массу технологий, включая работу с БД, с графикой DirectX, связью с серверами и интернетом, разработку на Андроид и IOS, и многое, многое другое.
____________________________________________________________________________________
P.S. все, о чем здесь написано, всего лишь измышления из головы.
совпадения с реальными людьми и фактами случайны.
Оценить эту запись

SQL запросы и параметры

Запись от krapotkin размещена 11.05.2018 в 10:37
Обновил(-а) krapotkin 23.05.2018 в 14:39

ВВЕДЕНИЕ. ЛИКБЕЗ
Как всегда, в тот момент, когда несколько подзадолбало отвечать на одни и те же вопросы, садишься и пишешь статью, чтобы потом не разжевывать самые азы, а эффектно пулять ссылкой)

Итак. Работа с БД - это то, из-за чего Делфи, как минимум, на десятилетие, стала сверхпопулярной.
Все просто и эффективно.
Концепция.
Есть сервера БД, которые предоставляют возможность к себе обращаться и забирать данные в программу.
Почти во всех случаях эти обращания идут на языке SQL.
Это обычная текстовая строка, в языке всего 4 команды, так что вам в любом случае придется немного почитать.
Команды INSERT UPDATE DELETE производят действия на сервере.
(Если вы "я в школе немецкий учил" и не представляете значение этих слов, то идите собирать деньги и покупать программиста, дальше вам все равно не пройти.)
Команда SELECT запрашивает данные с сервера, и результат запроса по частям(!) отдается вашей программе.
Это называется "набор данных" или "датасет".
Датасет - это набор строк (они же - записи), отвечающих условиям вашего запроса. Если условия не заданы, то вам придут вообще все данные. Не делайте так )))
К датасетам через компонент DataSource умеют подключаться разные DB-компоненты. DBEdit, DBLabel, DBGrid и т.д.
Пока что давайте в качестве образца датасета держать в уме какой-нибудь TQuery. Неважно TADOQuery, TDBQuery, TIBQuery, TFIBQuery. Сделаны они примерно одинаково.

Давайте разберем главные моменты, что вы должны понимать в датасете.

1. При запросе вы указываете ПОЛЯ датасета.
SQL
1
SELECT T.XXX, T.YYY FROM TABLE T WHERE T.ZZZ='12345'
К этим полям потом можно обращаться, например
Delphi
1
2
s := Query1.FieldByName('XXX').AsString;
n := Query1.FieldByName('YYY').AsInteger;
2. В один момент времени вы видите только ОДНУ запись датасета. Чтобы работать с другой, вы должны переместиться по датасету
Для этого служат методы First(), Last(), Next(), Prior() (Prior работает НЕ ДЛЯ ЛЮБОГО датасета)
Если вашу запись можно точно найти по значениям полей, пользуйтесь функцией Locate()
самая распространенная конструкция для работы это перебор всех записей датасета от начала до конца
Delphi
1
2
3
4
5
6
7
Query1.Open; // передать на сервер запрос, который хранится в Query1.SQL
while not Query1.EOF do // пока датасет не дошел до конца
begin
  // что-то делать с текущей записью
  Query1.Next(); // перейти на следующую запись
end;
Query1.Close; // закрыть датасет.
3. Ахтунг! Аттеншн! Внимание!
http://www.cyberforum.ru/blogs/469693/blog5259.html

Далее. То, ради чего вообще начал это писать
ПАРАМЕТРЫ В SQL-ЗАПРОСАХ
Всегда SQL запрос содержит какие-то условия. Что-то типа такого
SQL
1
SELECT T.Field1, T.Field2 FROM TABLE T WHERE T.Field3= 'Ваня'
на следующей строке нам нужно
SQL
1
SELECT T.Field1, T.Field2 FROM TABLE T WHERE T.Field3= 'Таня'
а потом
SQL
1
SELECT T.Field1, T.Field2 FROM TABLE T WHERE T.Field3= 'Федор'
первое, что приходит в голову, сделать строку, которая не меняется, и добавлять или вставлять в нее изменяющуюся часть
Delphi
1
2
3
4
Common:='SELECT T.Field1, T.Field2 FROM table T WHERE T.Field3=';
AName := QuotedStr('Таня'); // QuotedStr - строковые данные в запросе нужно обрамлять апострофами или кавычками
Query1.SQL.Text := Common + AName;
Query1.Open;
т.е. запрос будет каждый раз составляться заново. Рабочий вариант, но по нескольким причинам, выходящим за рамки статьи - не очень хороший.
Но, как обычно, все придумано до нас! В запрос можно вставлять ПАРАМЕТРЫ!
пишем один раз в начале программы (а еще лучше прямо в дизайнере формы)
Delphi
1
Query1.SQL.Text := 'SELECT T.Field1, T.Field2 FROM table T WHERE T.Field3=:param1';
вот param1 это и есть параметр
для такого случая нам нужно только заполнить значение параметра и переоткрыть датасет
Delphi
1
2
3
Query1.Close;
Query1.ParamByName('param1').AsString := 'Таня';
Query1.Open;
нужно сразу упомянуть, что для ADOQuery синтаксис немного отличается
там это происходит так
Delphi
1
2
Query1.Parameters.ParamByName('param1').Datatype := ftString;
Query1.Parameters.ParamByName('param1').Value := 'Таня';
При этом нас уже не волнует, что нужно ставить кавычки, апострофы или еще что-то. Движок сделает все за нас корректно.
Приведу пример, который 100500 раз заново спрашивали юные падаваны, не знающие страха, упрека и кнопки "Поиск".
Delphi
1
2
3
4
5
6
7
8
9
10
// где -то один раз в начале программы
ADOQuery1.SQL.Text := 'SELECT T.Field1, T.Field2 FROM table T WHERE T.DateField3 between :param1 and :param2';
ADOQuery1.Parameters.ParamByName('param1').Datatype := ftDate;
ADOQuery1.Parameters.ParamByName('param2').Datatype := ftDate; 
...
// а это там, где всё непосредственно происходит
ADOQuery1.Close;
ADOQuery1.Parameters.ParamByName('param1').Value := date1;
ADOQuery1.Parameters.ParamByName('param2').Value := date2;
ADOQuery1.Open;
для не-адо компонентов:
Delphi
1
2
3
4
5
6
Query1.SQL.Text := 'SELECT T.Field1, T.Field2 FROM table T WHERE T.DateField3 between :param1 and :param2';
...
Query1.Close;
Query1.ParamByName('param1').AsDateTime := date1;
Query1.ParamByName('param2').AsDateTime := date2;
Query1.Open;
Стоит запомнить еще одну вещь. Наш компонент DataSource следит за тем, как вносятся изменения в датасет, и при каждом изменении идет перерисовывать все компоненты, которые к нему подключены. Если ничего не предпринимать, то эта перерисовка станет главным пожирателем времени в вашей программе. Поэтому если нам нужно побегать по датасету, что-то поделать, и быстро, то нужно сделать такую конструкцию
Delphi
1
2
3
4
5
6
Query1.DisableControls; // отключаем перерисовку подключенных компонентов
try
  ... // работаем
finally
  Query1.EnableControls; // включаем обратно перерисовку подключенных компонентов
end;
Параметры работают и при вставке, изменении, удалении
Delphi
1
2
3
4
5
6
7
ADOQuery1.Close;
ADOQuery1.SQL.Text := 'INSERT INTO Tab3 (FIO, Bd) VALUES (:F,:D)';
ADOQuery1.Parameters.ParamByName('F').DataType := ftString;
ADOQuery1.Parameters.ParamByName('F').Value := Edit1.Text;
ADOQuery1.Parameters.ParamByName('D').DataType := ftDate;
ADOQuery1.Parameters.ParamByName('D').Value := DateTimePicker1.Date;
ADOQuery1.ExecSQL;
MASTER-DETAIL
Давайте разберем еще одну тему.
Как сделать, чтобы я выбирал строку с работником в гриде, а в другом гриде мне показывало все работы, связанные с этим работником. или что-то подобное.
Эта связь называется мастер-деталь. В делфи есть встроенное средство для таких штук, но всегда лучше понимать как это работает и делать руками...
Итак, у нас есть два запроса. первый - список работников, второй - список выполненных работ.
Допустим, в первом условий вообще нет. Если даже есть они задаются где-то там и не относятся к нашей задаче.
А вот условия во втором датасете - работы только для выбранного работника.
Если хоть чучуть почитать про БД, или просто раскинуть мозгами, мы придумаем, что у каждого работника есть уникальный код. Его даже на экран выводить не нужно, но в таблице он должен быть. назовем его без придумок - ID.
В таблице работ должно быть поле, которое ссылается на этот ID, например WORKER_ID
Тогда формулируем задачу.
При каждом перемещении по датасету qWorkers переоткрывать датасет qWorks c уcловием WORKER_ID равен текущему значению qWorkers.ID.
Решение.
qWorks.SQL выглядит как то так
SQL
1
SELECT ....  FROM WORKS W WHERE W.WORKER_ID=:worker_id
В событии qWorkers.OnAfterScroll пишем код из трех строчек
для ADO
Delphi
1
2
3
4
5
6
7
8
9
// это не в AfterScroll, а как всегда, где-то одноразово пораньше
qWorks.Parameters.ParamByname('worker_id').datatype := ftInteger;
...
procedure TForm1.qWorkersAfterScroll(Sender:TObject);
begin
  qWorks.Close;
  qWorks.Parameters.ParamByname('worker_id').Value := qWorkers.FieldByName('ID').AsInteger;
  qWorks.Open;
end;
для не-ADO просто
Delphi
1
2
3
4
5
procedure TForm1.qWorkersAfterScroll(Sender:TObject);
begin
  qWorks.Close;
  qWorks.ParamByname('worker_id').AsInteger := qWorkers.FieldByName('ID').AsInteger;
  qWorks.Open;
P.S. перестаньте давать русские имена ,а еще и с пробелами, таблицам, базам и полям. Ни одна нормальная БД вас не поймет, а вы будете потом засыпать форумы вопросами что за ошибка в запросе
SQL
1
SELECT моё дебильное название поля FROM таблица тоже огонь


в заключение приведу ссылку для тех, у кого ADO. У меня его нет, поэтому если что-то не так, смотрите там и ищите сами
http://devdelphi.ru/?p=16
Размещено в Без категории
Просмотров 720 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru