Форум программистов, компьютерный форум, киберфорум
Наши страницы
C++ Builder: Базы данных
Войти
Регистрация
Восстановить пароль
 
Lord_Voodoo
Супер-модератор
8610 / 2241 / 133
Регистрация: 07.03.2007
Сообщений: 10,830
Завершенные тесты: 1
1

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

11.05.2018, 18:13. Просмотров 345. Ответов 4
Метки нет (Все метки)

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

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

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

1. При запросе вы указываете ПОЛЯ датасета. К этим полям потом можно обращаться, например
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. Ахтунг! Аттеншн! Внимание!
Все DB-компоненты ОТОБРАЖАЮТ текущую запись и поле датасета, к которому они подключены. НЕ ХРАНЯТ!!!
Они являются элементами ИНТЕРФЕЙСА - т.е. штуками для работы пользователя! Не программиста!!!
Нет смысла брать текст из DBEdit, его надо брать из поля датасета, к которому этот DBEdit подключен.
Если узнаю, что вы так делаете, оборву что-нибудь важное!

Далее. То, ради чего вообще начал это писать
ПАРАМЕТРЫ В 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 := dtString;
Query1.Parameters.ParamByName('param1').Value := 'Таня';
При этом нас уже не волнует, что нужно ставить кавычки, апострофы или еще что-то. Движок сделает все за нас корректно.
Приведу пример, который 100500 раз заново спрашивали юные падаваны, не знающие страха, упрека и кнопки "Поиск".
Delphi
1
2
3
4
5
6
7
8
9
10
// где -то один раз в начале программы
Query1.SQL.Text := 'SELECT T.Field1, T.Field2 FROM table T WHERE T.DateField3 between :param1 and :param2';
Query1.Parameters.ParamByName('param1').Datatype := dtDate;
Query1.Parameters.ParamByName('param2').Datatype := dtDate; 
...
// а это там, где всё непосредственно происходит
Query1.Close;
Query1.Parameters.ParamByName('param1').Value := date1;
Query1.Parameters.ParamByName('param2').Value := date2;
Query1.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;
MASTER-DETAIL
Давайте разберем еще одну тему.
Как сделать, чтобы я выбирал строку с работником в гриде, а в другом гриде мне показывало все работы, связанные с этим работником. или что-то подобное.
Эта связь называется мастер-деталь. В делфи есть встроенное средство для таких штук, но всегда лучше понимать как это работает и делать руками...
Итак, у нас есть два запроса. первый - список работников, второй - список выполненных работ.
Допустим, в первом условий вообще нет. Если даже есть они задаются где-то там и не относятся к нашей задаче.
А вот условия во втором датасете - работы только для выбранного работника.
Если хоть чучуть почитать про БД, или просто раскинуть мозгами, мы придумаем, что у каждого работника есть уникальный код. Его даже на экран выводить не нужно, но в таблице он должен быть. назовем его без придумок - ID.
В таблице работ должно быть поле, которое ссылается на этот ID, например WORKER_ID
Тогда формулируем задачу.
При каждом перемещении по датасету qWorkers переоткрывать датасет qWorks c уcловием WORDKER_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 := dtInteger;
...
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 таблица тоже огонь
Источник
3
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
11.05.2018, 18:13
Ответы с готовыми решениями:

Запросы SQL
Велико уважаемые знатаки, у меня к вам вопрос. Я програмно задаю запрос SQL ...

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

SQL запросы
Возникла проблема с SQL запросами. Есть таблица REPOR, с полями ADRES, VREMY,...

Параметры в SQL запросе
вот такой вот запрос SELECT Remont.Id_tt, Remont.D_priem, Tovar.Id_t,...

SQL-запросы из Access
как Вы считаете, какой лучше sql-запрос сделать, используя данные из самой...

4
Bit_Man
423 / 353 / 92
Регистрация: 24.04.2012
Сообщений: 1,399
Записей в блоге: 3
15.05.2018, 06:24 2
Уважаемые. Разъясните почему Вы для себя выбрали именно первый вариант?
Цитата Сообщение от Lord_Voodoo Посмотреть сообщение
для не-адо компонентов
Delphi
1
Query1.ParamByName('param1').AsDateTime := date1;
Когда можно сделать так
C++
1
Query->ParamByName('param1')->Value = date1;
А как же передача Null?
C++
1
Query->ParamByName('param1')->Value = Null();
0
Lord_Voodoo
Супер-модератор
8610 / 2241 / 133
Регистрация: 07.03.2007
Сообщений: 10,830
Завершенные тесты: 1
16.05.2018, 13:57  [ТС] 3
Цитата Сообщение от Bit_Man Посмотреть сообщение
А как же передача Null?
Одним из вариантов решения видел ваш вариант:
C++
1
Query->ParamByName('param1')->Value = Null();
Но в одном из пакетов, не помню названия, работал такой вариант:
C++
1
Query->ParamByName('param1')->Clear();
Хотя, как по мне, при формировании запроса следует вообще не включать поля, в которые необходимо записать NULL-значение.

Данный вариант, ИМХО, более понятнее и нагляднее смотрится:
C++
1
Query1.ParamByName('param1').AsDateTime := date1;
чем этот:
C++
1
Query->ParamByName('param1')->Value = date1;
0
Bit_Man
423 / 353 / 92
Регистрация: 24.04.2012
Сообщений: 1,399
Записей в блоге: 3
17.05.2018, 05:10 4
Цитата Сообщение от Lord_Voodoo Посмотреть сообщение
Хотя, как по мне, при формировании запроса следует вообще не включать поля, в которые необходимо записать NULL-значение
Не соглашусь. Например, при запросе на изменение данных на значение параметра накладывается условие.
C++
1
2
3
4
5
6
int ParamValue = 0;
//далее где-то заполняется и при заполнении параметров
if (ParamValue != 0)
  Query->ParamByName('param1')->Value = ParamValue;
else
  Query->ParamByName('param1')->Value = Null();

Не по теме:

Кто-то скажет, что можно это и в базе обработать, но мы разговариваем о клиентской части.

0
Lord_Voodoo
Супер-модератор
8610 / 2241 / 133
Регистрация: 07.03.2007
Сообщений: 10,830
Завершенные тесты: 1
17.05.2018, 08:25  [ТС] 5
Bit_Man, именно так и скажу:
Цитата Сообщение от Bit_Man Посмотреть сообщение
Кто-то скажет, что можно это и в базе обработать, но мы разговариваем о клиентской части.
нужно максимально нагружать работой сервер БД, а не клиентскую часть системы...

Тему только начали создавать, поэтому много вопросов не охвачено, если есть чего сказать, пишите в личку. Тема для обсуждении закрыта.
0
17.05.2018, 08:25
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
17.05.2018, 08:25

Access + sql запросы
привет народ! хочу добавить строку в базу данных: ADOQuery1 -> SQL ->...

SQL запросы и соединение с БД
Привет всем программистам! Прошу вашей помощь с возникшей проблемой...А именно...

SQL запросы в цикле
База данных содержит поле с числовыми значениями. База выводится на форму через...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
5
Закрытая тема Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru