ADODB : Как передать пакет запросов?

1. SirStefan 53 27.06.16 18:52 Сейчас в теме
Подскажите, реально ли для MySQL через ADODB выполнить пакет вида
UPD ATE tbl SET name='один' WHERE id = '1';
UPD ATE tbl SET name='два' WHERE id = '2';
UPDATE tbl SE T name='три' WHERE id = '3';...

Попытка запихать запрос такого вида в АДО.Execute(); приводит к ошибке
[MySQL][ODBC 5.3(w) Driver][mysqld-5.1.73]You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'UPDATE tbl SE T...
А выполнение построчно очень долгое.
По теме из базы знаний
Ответы
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
2. cool.vlad4 2 28.06.16 14:28 Сейчас в теме
(1) SirStefan, реально . проверено. ты наверное неверно формируешь запрос.(синтаксическая ошибка). но я посоветую сделать по другому, значительно быстрее сделать временную таблицу, потом update через join
3. cool.vlad4 2 28.06.16 14:30 Сейчас в теме
(1) SirStefan, также советую обрамлять имена полей, таблиц через `` (backtick), например `field` .
4. SirStefan 53 28.06.16 15:40 Сейчас в теме
(3), имена через кавычки не помогает.
А как сделать временную таблицу и заполнить ее одним запросом?
5. cool.vlad4 2 28.06.16 16:11 Сейчас в теме
(4) SirStefan, через CRE ATE TABLE либо CREATE TEMPORARY TABLE , просто я не знаю какую конкретно табличку надо, в смысле какие поля. но в интернете можно найти очень много мануалов по описанию данного выражения.(например можно вообще лениво поступить CREATE TEMPORARY TABLE temptable SEL ECT * FR OM tbl LIMIT 0; в итоге будет пустая временная таблица идентичной структуры исходной таблицы) вставка это уже в зависимости от того в каком виде входные данные. можно вообще одной командой практически импортировать csv файл (http://dev.mysql.com/doc/refman/5.7/en/load-data.html) либо формировать команды INS ERT INTO temptable (`field1`,`field2`) VALUES (value11,value21),(value12,value22),(val ue13,value23) и т.д. (опытным путем установить сколько оптимально этих VALUES). Затем сделать UPD ATE tbl table1 JOIN temptable tt ON table1.id=tt.id SE T table1.`name`=tt.name; и это действительно будет значительно быстрее чем одиночные update
6. SirStefan 53 28.06.16 17:32 Сейчас в теме
(5), про виртуальную таблицу попробую, спасибо. Но я имел в виду, что ее все равно не получится сформировать одним пакетным запросом. Не воспринимает ODBC драйвер почему то ";". При этом из админки все пакетные запросы отрабатывают на ура. Вот и пытаюсь понять в чем дело тут может быть.
INSERT у меня так и реализован - одним запросом передаются все данные. А вот с UPDATE уже так не выходит.
7. cool.vlad4 2 28.06.16 17:39 Сейчас в теме
(6) SirStefan, у меня отрабатывает ADODB с ; то что я описывал это проверенное решение. тут дело в чем-то ином. (если запрос из под админки работает, а из 1С + ADODB не работает, смею предположить, что быть может с кодировками что-то не так, надо причину искать)
8. SirStefan 53 29.06.16 05:47 Сейчас в теме
Перепроверил все уже. И ANSI и UNICODE варианты драйвера. И cp1251. И переменные в кавычках

АДО = Новый COMОбъект("ADODB.Connection");
АДО.ConnectionTimeout = 30;
АДО.CursorLocation = 3;
АДО.Open("driver={MySQL ODBC 5.3 Unicode Driver};server=<>;Database=<>;uid=<>;pwd=<>");
АДО.Execute("INSЕRT INTО tbl (id,name) VALUES ('0001','РАЗ'); INSЕRT INTО tbl (id,name) VALUES ('0002','ДВА')");

Не работает.
Произошла исключительная ситуация (Microsoft OLE DB Provider for ODBC Drivers): [MySQL][ODBC 5.3(w) Driver][mysqld-5.1.73]You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSЕRT INTО tbl (id,name) VALUES ('0002','ДВА')' at line 1

Просто INSЕRT INTО tbl (id,name) VALUES ('0001','РАЗ') работает отлично.

P.S. INSЕRT ставил только для проверки запроса. Интересует как заставить работать UPDATE
9. cool.vlad4 2 29.06.16 12:54 Сейчас в теме
(8) SirStefan, а какая версия Mysql? (не драйвера, а версия СУБД) . и еще надо попробовать разделить команды символом новой строки.
"INSЕRT INTО tbl (id,name) VALUES ('0001','РАЗ');"+ Символы.ПС+ " INSЕRT INTО tbl (id,name) VALUES ('0002','ДВА')"
ps в принципе можно и таким образом вставлять во временную таблицу "INSЕRT INTО tbl (id,name) VALUES ('0001','РАЗ'), ('0002','ДВА');" это будет быстрее. затем сделать одной командой update. выполнять команды отдельно, необязательно их пытаться запихнуть в один большой запрос.
10. spenser123 29.06.16 12:57 Сейчас в теме
А последовательно почему не выполнить?
11. cool.vlad4 2 29.06.16 12:58 Сейчас в теме
(10) spenser123, я тоже не понимаю. в (5) шла речь о последовательном выполнении.
15. SirStefan 53 29.06.16 17:08 Сейчас в теме
(10), Потому что для десятков тысяч записей все работает очень медленно.
16. cool.vlad4 2 29.06.16 17:23 Сейчас в теме
(15) SirStefan, да потому что update ты одиночные делаешь. поэтому. а вовсе не из-за последовательного выполнения команд в принципе. ты ж даже не пробовал , что я тебе писал. в блоге по ссылке, что я давал, там вообще запрос менее секунды исполняется. у меня из практики 100 000 update аналогично очень быстро выполнялись.
17. cool.vlad4 2 29.06.16 17:28 Сейчас в теме
(15) SirStefan, вот такой говнокод сработает не просто очень медленно, а очень даже быстро. (в качестве временной таблицы в данному примере обычная таблица, поэтому она очищается вначале)
ConnectionString   =  "некая строка соединения";
	
	Соединение = Новый COMОбъект("ADODB.Connection");
	Соединение.ConnectionString = ConnectionString;
	Попытка
		Соединение.Open();
	Исключение
		ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки());
	КонецПопытки;	
	Попытка
		Соединение.Execute("truncate table temptable;");
		Соединение.Execute("ALT ER   TABLE temptable AUTO_INCREMENT = 1");
	Исключение
		ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки());
		Возврат;
	КонецПопытки;
	
	Текст = Новый ТекстовыйДокумент;
	Текст.ДобавитьСтроку("INS ERT IN TO temptable (`field1`,`field2`) VALUES ");
	Для i=0 По ТЗ.Количество()-2 Цикл
		Строка=ТЗ[i];
		Если i<>0 И i%100=0 Тогда
			ТекстЗапроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("('%1','%2')",Строка.field1,Формат(Строка.field2,"ЧРД=.; ЧГ="));
			Текст.ДобавитьСтроку(ТекстЗапроса);
			Текст.ДобавитьСтроку(";");
			Текст.ДобавитьСтроку(Символы.ПС);
			Попытка
				Соединение.Execute(Текст.ПолучитьТекст());
			Исключение
				ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки());
				Прервать;
			КонецПопытки;
			Текст = Новый ТекстовыйДокумент;
			Текст.ДобавитьСтроку("INS ERT IN TO temptable (`field1`,`field2`) VALUES ");
		Иначе
			ТекстЗапроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("('%1','%2')",Строка.field1,Формат(Строка.field2,"ЧРД=.; ЧГ="));
			Текст.ДобавитьСтроку(ТекстЗапроса+",");
		КонецЕсли; 
	КонецЦикла;
	Строка=ТЗ[ТЗ.Количество()-1];
	ТекстЗапроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("('%1','%2')",Строка.field1,Формат(Строка.field2,"ЧРД=.; ЧГ="));
	Текст.ДобавитьСтроку(ТекстЗапроса);
	Текст.ДобавитьСтроку(";");
	Попытка
		Соединение.Execute(Текст.ПолучитьТекст());
	Исключение
		ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки());
		Возврат;
	КонецПопытки;
	
	Попытка
		Соединение.Execute("UPD ATE tbl table1 JOIN temptable ttt ON table1.field1=ttt.field1 SE T p.field2=ttt.field2;");
	Исключение
		ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки());
		Возврат;
	КонецПопытки;

Показать
12. spenser123 29.06.16 13:10 Сейчас в теме
ИЛИ по конструкции вида
UPD ATE tbl SE T name=CASE WHEN id = '1' THEN 'один'
                   WHEN  id = '2'  THEN 'два' 
                   WHEN  id = '3'  THEN'три'
                END  
WHERE id in ('1','2','3')
13. cool.vlad4 2 29.06.16 14:18 Сейчас в теме
(12) spenser123, можно, конечно, но только у него наверное все таки не три записи , а больше. и по скорости имхо это не будет быстрее. самому мне проверять лениво, тем более нет начальных условий, но вот например сравнение https://explainextended.com/2009/08/18/passing-parameters-in-mysql-in-list-vs-temporary-table/
14. spenser123 29.06.16 15:43 Сейчас в теме
Ну я ответ на первый вопрос дал, как сделать не используя построчно - в этом случае 1 execute в 1С будет, а результат именно тот который дали бы 3 запроса из того же первого сообщения построчно.
18. dlightman 13.10.19 21:37 Сейчас в теме
Прошло больше трех лет с вопроса, но ответа нет и не только на этом форуме. Поэтому мне пришлось изучить вопрос и я доложу о результатах.

Во-первых SirStefan совершенно прав. Проблема в MySQL, т.к. он не умеет выполнить в одном запросе пакет из нескольких самостоятельных запросов. Это неизменно ведет к сообщению о синтаксической ошибке, которой нет. Автор также прав, утверждая что пакетный запрос выполнится гораздо быстрей, в разы и даже в порядки быстрей, поэтому эффективной замены ему нет.

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

1. удаление процедуры на случай если она уже есть
DR OP PROCEDURE IF EXISTS myProc

2. создание процедуры
CRE ATE PROCEDURE myProc ()
BEGIN
UPD ATE tbl SE T name='один' WHERE id = '1';
UPDATE tbl SE T name='два' WHERE id = '2';
UPDATE tbl SE T name='три' WHERE id = '3';
...
END

3. вызов процедуры
CALL myProc

4. удаление процедуры
DR OP PROCEDURE IF EXISTS myProc

P.s. редактор поста за каким-то бесом портит код вставляя пробелы, надеюсь вы поймете где именно.
19. OerlandHue 21.11.19 03:22 Сейчас в теме
Решение выше не является оптимальным и верным.
С некоторой версии, по-моему с 5, можно выполнять пакеты запросов через ADODB для драйвера MySQL ODBC.
Для этого нужно в строке соединения указать параметр MULTI_STATEMENTS = 1;
Пример:
СтрокаСоединения = СтрШаблон("DRIVER={MySQL ODBC 5.3 ANSI Driver}; SERVER=%1; DATABASE=%2; PORT=%3; UID=%4; PWD=%5; MULTI_STATEMENTS=1", Хост, БазаДанных, Порт, Пользователь, Пароль);


Поддерживается вроде как с версии 5 MySQL.
Документация https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-configuration-connection-parameters.html#codbc-dsn-option-flags.

Пример, на котором проверялось:
Command = Новый COMОбъект("ADODB.Command");
Command.ActiveConnection = DBConnection;	
			
ТекстЗапроса = "START TRANSACTION;
|	DELETE FROM random_table ;
|	INS ERT IN TO random_table (id, psw) VALUES ";
			
ЭтоПервый = Истина;
Для Каждого СтрокаБейджа Из ТаблицаБейджейКассиров Цикл
				
	Если ЭтоПервый Тогда
		ЭтоПервый = Ложь;
	Иначе
		ТекстЗапроса = ТекстЗапроса + ", ";	
	КонецЕсли;
				
	КодКассира = Формат(СтрокаБейджа.ИдентификаторСупермаг, "ЧГ=");	
				
	ТекстЗначения = СтрШаблон("(%1, OLD_PASSWORD(""%2""))", КодКассира, СтрокаБейджа.ПарольНаБейдже);					
	ТекстЗапроса = ТекстЗапроса + ТекстЗначения;
				
КонецЦикла; 
			
ТекстЗапроса = ТекстЗапроса + ";
			| COMMIT;";	
			
Command.CommandText  = ТекстЗапроса;
Command.Execute();
Показать
1giga; Aleskey_K; dlightman; +3 Ответить
20. dlightman 08.07.20 02:15 Сейчас в теме
(19) ниже OerlandHue сделал замечание на счет параметра MULTI_STATEMENTS=1; которое я проверил и реально признаю, что так необходимость в создании процедуры отпала.
Оставьте свое сообщение

Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот