Russian Qt Forum

Qt => Базы данных => Тема начата: vladimirse от Март 10, 2017, 11:39



Название: Выполнение встроенных процедур MS SQL Server
Отправлено: vladimirse от Март 10, 2017, 11:39
Доброго времени суток, уважаемые!
Есть необходимость обратиться к БД по средством встроенной процедуры, увы другие варианты не возможны.
СУБД MS SQL. В ms sql management studio процедура выглядит следующим образом (с примером вводимых параметров):
Код
SQL
USE [OIK]
GO
 
DECLARE @return_value int
EXEC @return_value = [dbo].[Step]
           @Cat = N'I',
           @Ids = N'30568',
           @Start = N'2017-03-01 11:00:00.000',
           @Stop = N'2017-03-01 14:30:00.000',
           @Step = 60
 
SELECT 'Return Value' = @return_value
 
GO
 

Не могу догнать как ее выполнить в QT. Пытаюсь следующим образом:
Код
C++ (Qt)
...
if (db.open())
   {
       QString q;
       QSqlQuery query(db);
       q.sprintf("CALL dbo.Step(%s, %s, %s, %s, %d)", "S", "11", "2017-03-01 11:00:00.000", "2017-03-05 00:00:00.000", 60);
       qDebug() << query.exec(q);
}
 

Соответственно получаю "false".
Подскажите пожалуйста как это правильно реализовать.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 10, 2017, 11:55
Никогда так не делай. Используй QSqlQuery::addBindValue.

Код
C++ (Qt)
query.prepare("Call dbo.Step(?, ?, ?, ?, ?)");
query.addBindValue("S");
....
query.exec();
 


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 10, 2017, 11:57
По поводу самого sql ничего не могу сказать, не вызывал хранимки в мускуле. В постгресе я бы сделал: "SELECT procedure_name(?, ?, ?, ?, ?)"


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: vladimirse от Март 10, 2017, 12:07
Никогда так не делай. Используй QSqlQuery::addBindValue.

Код
C++ (Qt)
query.prepare("Call dbo.Step(?, ?, ?, ?, ?)");
query.addBindValue("S");
....
query.exec();
 

Большое спасибо, исправил!
Код
C++ (Qt)
QSqlQuery query(db);
query.prepare("CALL dbo.Step ( ?, ?, ?, ?, ? )");
query.addBindValue("S");
query.addBindValue("11");
query.addBindValue("2017-03-01 11:00:00.000");
query.addBindValue("2017-03-05 00:00:00.000");
query.addBindValue(60);
qDebug() << query.exec();
 
Но пока, увы, не работает. :-(


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 10, 2017, 12:09
Код
C++ (Qt)
qDebug () << query.lastErro().text()
 


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 10, 2017, 12:09
Дату/Время передавай не строкой, а при помощи QDateTime. addBindValue понимает типы и корректно их передает в БД.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: vladimirse от Март 10, 2017, 15:23
Дату/Время передавай не строкой, а при помощи QDateTime. addBindValue понимает типы и корректно их передает в БД.

Тут все таки проблема видимо в другом, нашел процедуру без параметров, выполняю:
Код
C++ (Qt)
QSqlQuery query(db);
query.exec("call [dbo].[GetCurrentDate_ForKU]"); // пробовал различные варианты записи
qDebug () << query.lastError().text();
 


Выдает:
Код:
QTDS: General SQL Server error: Check messages from the SQL Server 
В логах на сервере пусто.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 10, 2017, 15:32
1. Простые запросы работают?
2. В консоли mysql запрос "call [dbo].[GetCurrentDate_ForKU]" срабатывает?


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: vladimirse от Март 10, 2017, 15:47
1. Простые запросы работают?
2. В консоли mysql запрос "call [dbo].[GetCurrentDate_ForKU]" срабатывает?

Разобрался. Работает вот так:
Код
C++ (Qt)
QSqlQuery query(db);
query.setForwardOnly(true);
query.prepare("exec [dbo].[Step] ?, ?, ?, ? ,?");
query.addBindValue("B");
query.addBindValue("11");
query.addBindValue("2017-03-01 11:00:00.000");
query.addBindValue("2017-03-05 00:00:00.000");
query.addBindValue("60");
query.exec();
 

Большое спасибо за помощь!


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 10, 2017, 15:58
1. Какой-то очень странный синтаксис. Уверен, что нельзя как-то типа "SELECT Step(?, ?, ?, ?, ?)"?
2. Не используй строки, используй типы - это очень важно.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: titan83 от Март 12, 2017, 14:35
Извините, не удержался))
"Не используй строки Люк, используй типы - это путь силы." (с) Мастер Йода.
А вообще мне лично так нравится больше:
Код:
    for (int i = 0; i < queryParameters.size(); i++)
        lQueryText.replace(queryParameters.keys().at(i), queryParameters.value(queryParameters.keys().at(i)));
где
lQueryText - QString
queryParameters - QHash<QString, QString>
И душевно работает без QSqlQuery::prepare
И строки использовать намного удобнее, чем over9000 типов, все становится единообразно. Хотя, если оплата за количество строк кода, то смысл есть))


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Old от Март 12, 2017, 15:42
А вообще мне лично так нравится больше:
Только это ужасно не эффективно.
Вы для функции replace дважды вызываете queryParameters.keys(), который формирует список ключей пробегаясь по всей коллекции и заполняя список. И так для каждого запроса.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: titan83 от Март 12, 2017, 17:03
А вообще мне лично так нравится больше:
Только это ужасно не эффективно.
Вы для функции replace дважды вызываете queryParameters.keys(), который формирует список ключей пробегаясь по всей коллекции и заполняя список. И так для каждого запроса.
Что поделать... Все высокоуровневое программирование ужасно неэффективно, по сравнению с ассемблером (я сравнилал бейсик и асм на z80).
Но мне важнее написать две строки кода, а не десять, даже в ущерб эффективности.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Old от Март 12, 2017, 17:20
Что поделать... Все высокоуровневое программирование ужасно неэффективно, по сравнению с ассемблером (я сравнилал бейсик и асм на z80).
Ну если сравнивать бейсик и ассемблер, то да. А так нет. :)

Но мне важнее написать две строки кода, а не десять, даже в ущерб эффективности.
Напрасно. Это можно написать в три строки (вместо двух) без ущерба для эффективности.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: titan83 от Март 12, 2017, 18:25
Что поделать... Все высокоуровневое программирование ужасно неэффективно, по сравнению с ассемблером (я сравнилал бейсик и асм на z80).
Ну если сравнивать бейсик и ассемблер, то да. А так нет. :)

Но мне важнее написать две строки кода, а не десять, даже в ущерб эффективности.
Напрасно. Это можно написать в три строки (вместо двух) без ущерба для эффективности.
Вероятно, лет через 15 я с вами соглашусь ;) а пока мне это видится нерациональной операцией (больше 4(!) параметров в запросы я пока не передаю).
Автор, извини за флуд, Old, предлагаю закрыть этот небольшой офф.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 13, 2017, 08:44
Извините, не удержался))
"Не используй строки Люк, используй типы - это путь силы." (с) Мастер Йода.
А вообще мне лично так нравится больше:
Код:
    for (int i = 0; i < queryParameters.size(); i++)
        lQueryText.replace(queryParameters.keys().at(i), queryParameters.value(queryParameters.keys().at(i)));
где
lQueryText - QString
queryParameters - QHash<QString, QString>
И душевно работает без QSqlQuery::prepare
И строки использовать намного удобнее, чем over9000 типов, все становится единообразно. Хотя, если оплата за количество строк кода, то смысл есть))
Это небезопасно и может привести к sql инъекции. Плюс, при использовании биндов не надо ничего экранировать.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: titan83 от Март 13, 2017, 12:30
Извините, не удержался))
"Не используй строки Люк, используй типы - это путь силы." (с) Мастер Йода.
А вообще мне лично так нравится больше:
Код:
    for (int i = 0; i < queryParameters.size(); i++)
        lQueryText.replace(queryParameters.keys().at(i), queryParameters.value(queryParameters.keys().at(i)));
где
lQueryText - QString
queryParameters - QHash<QString, QString>
И душевно работает без QSqlQuery::prepare
И строки использовать намного удобнее, чем over9000 типов, все становится единообразно. Хотя, если оплата за количество строк кода, то смысл есть))
Это небезопасно и может привести к sql инъекции. Плюс, при использовании биндов не надо ничего экранировать.
Каким образом bind защищает от инъекции? Удаляет sql запросы, соответствующие диалекту используемой БД?
Про экранирование - спасибо, это существенная информация.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: Пантер от Март 13, 2017, 13:03
Код:
query.exec(QString ("SELECT * FROM table_name WHERE id=%1").arg(idString));

Если в idString передать: "0; DROP TABLE table_name", то удалится таблица. Бинд это разрулит, ибо такая строка точно не закастится в тип id. Бинд работает на низком уровне, он не преобразуется в строку внутри Кьюта, а передается драйверу БД.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: titan83 от Март 13, 2017, 16:30
Код:
query.exec(QString ("SELECT * FROM table_name WHERE id=%1").arg(idString));

Если в idString передать: "0; DROP TABLE table_name", то удалится таблица. Бинд это разрулит, ибо такая строка точно не закастится в тип id. Бинд работает на низком уровне, он не преобразуется в строку внутри Кьюта, а передается драйверу БД.
Благодарю.


Название: Re: Выполнение встроенных процедур MS SQL Server
Отправлено: twp от Март 21, 2017, 16:41
Извините, не удержался))
"Не используй строки Люк, используй типы - это путь силы." (с) Мастер Йода.
А вообще мне лично так нравится больше:
Код:
    for (int i = 0; i < queryParameters.size(); i++)
        lQueryText.replace(queryParameters.keys().at(i), queryParameters.value(queryParameters.keys().at(i)));
где
lQueryText - QString
queryParameters - QHash<QString, QString>
И душевно работает без QSqlQuery::prepare
И строки использовать намного удобнее, чем over9000 типов, все становится единообразно. Хотя, если оплата за количество строк кода, то смысл есть))
Это небезопасно и может привести к sql инъекции. Плюс, при использовании биндов не надо ничего экранировать.

Есть еще вот что. СУБД кэширует и оптимизирует (прекомпилирует) все выполняемые запросы. Соответственно, когда в базу передается на выполнение один и тот же запрос, но с разными параметрами (указанными непосредственно в запросе), то база каждый раз будет интерпретировать такой запрос как новый, со своими издержками. Это не есть хорошо, особенно если в это время к базе поступают много запросов одновременно. Поэтому правильно передавать именно шаблон запроса через QSqlQuery::prepare. QSqlQuery::prepare как раз дает команду СУБД извлечь из кэша существующий или создать и оптимизировать новый запрос. Ну и уже потом в запрос передаются параметры через QSqlQuery::bindValue. Т.е. по сути такие запросы очень близки к хранимым процедурам.