Я пытаюсь выполнить запрос, используя беглый конструктор запросов Laraval. Запрос включает в себя пользовательскую функцию в части SELECT. Он работает с очень простым запросом, но при более крупных запросах наличие пользовательского запроса приводит к разрыву соединения.
Вот простой запрос, который работает:
$query = DB::connection('crm')
->table('contacts')
->where('id', '=', '6eb31b38-b813-5d5c-d33d-52ab2498835b')
->addSelect('id', 'first_name', 'last_name')
->addSelect(DB::raw('my_contact_emails(id, 1) as email'))
;
$row = $query->first();
Функция базы данных my_contact_emails () извлекает одно или несколько сообщений электронной почты для contact.id в виде одной строки, разделенной запятыми. У меня нет проблем с этим меньшим запросом.
У меня также есть запрос большего размера, который объединяет около 12 таблиц и имеет около 20 столбцов, которые он возвращает. Это тоже хорошо работает, пока пользовательская функция не используется в нем. Как только я добавляю пользовательскую функцию, как показано в меньшем запросе выше, база данных MySQL сообщает об этой ошибке в свой журнал, и соединение закрывается:
140916 23:25:55 [Предупреждение] Прервано соединение 2625770 с БД: ‘my_db’
пользователь: ‘my_user’ хост: ‘my.server.co.uk’ (Ошибка чтения
пакеты связи)
Это использует Laravel PDO, с сопоставлением utf8_general_ci по всем направлениям (все таблицы, все столбцы, драйвер базы данных Laravel).
Если я возьму запрос, сгенерированный конструктором запросов, и вставлю его в PHPmyAdmin, то он будет работать нормально. То же самое и с MySQL Workbench. Но в драйвере PDO Laravel 4.2 эта ошибка видна на сервере базы данных. Сам Laravel не сообщает об ошибках — он просто возвращает ноль строк.
Изменить 1:
Изменить 2:
Изменить 3:
Эти правки удалены (4 и 5 покрывают это)
Изменить 4:
Laravel имеет несколько параметров, которые он устанавливает при создании объекта PDO. Это один:
PDO::ATTR_EMULATE_PREPARES => false
Именно поэтому пользовательская функция базы данных в предложении SELECT уничтожает соединение с базой данных. Если я установлю значение true или закомментирую, запрос будет работать. Я выясню, для чего предназначен этот вариант PDO и почему он должен вызывать эту проблему.
Поэтому, если я правильно понимаю, сброс PDO :: ATTR_EMULATE_PREPARES на false всегда будет сначала идти к драйверу MySQL для подготовки операторов. В моем случае это как-то не в состоянии правильно подготовить оператор, а затем MySQL падает, когда пытается выполнить оператор. Мое решение состоит в том, чтобы эмулировать prepare (установите его в true), что (я думаю) означает, что PDO будет создавать полный оператор SQL, а не базу данных MySQL. Я опубликую это как ответ, когда буду более полно понимать, что происходит, почему это не удается, почему эмуляция заставляет его работать, и что должно действительно быть сделано, чтобы заставить это работать.
Изменить 5: Вот два запроса, которые показывают потерянное соединение происходит или не происходит. Я использую простой PDO prepare () / execute () / fetchAll (), чтобы Laracel вышел из цикла. Все, что помог Ларавел, было PDO::ATTR_EMULATE_PREPARES
установка после того, как запрос был построен.
В обоих запросах выбранная часть:
SELECT my_contact_emails(c.id, 1) as email
Короткий запрос выглядит так:
FROM contacts as c limit 10
Более длинный запрос выглядит так:
from `contacts` as `c`
inner join `contacts_cstm` as `cc` on `cc`.`id_c` = `c`.`id`
inner join `sw_bookings_contacts_c` as `bc` on `bc`.`sw_booking50e1ontacts_idb` = `c`.`id` and `bc`.`deleted` = ?
inner join `sw_bookings` as `b` on `bc`.`sw_booking50a3ookings_ida` = `b`.`id` and `b`.`deleted` = ?
inner join `sw_bookings_cstm` as `b_cstm` on `b_cstm`.`id_c` = `b`.`id`
inner join `sw_bookingsw_expedition_c` as `be` on `be`.`sw_bookinga016ookings_idb` = `b`.`id` and `be`.`deleted` = ?
inner join `sw_expedition` as `e` on `be`.`sw_booking29d8edition_ida` = `e`.`id` and `e`.`deleted` = ? and `e`.`status` = ?
inner join `erp_erplookup` as `el_type` on `el_type`.`contact_type` = `cc`.`type_c` and `el_type`.`deleted` = ? and `el_type`.`lookup_type` = ?
left join `erp_erplookup` as `erp_l` on `erp_l`.`salutation` = `c`.`salutation` and `erp_l`.`deleted` = ? and `erp_l`.`lookup_type` = ?
left join `sw_country_regions` as `crs` on `crs`.`id` = `c`.`primary_address_state` and `crs`.`deleted` = ?
left join `sw_country_regions_cstm` as `crs_cstm` on `crs_cstm`.`id_c` = `crs`.`id`
left join `sw_country_regions` as `cr` on `c`.`primary_address_state` = `cr`.`code`
left join `sw_country_regions_cstm` as `crc` on `crc`.`id_c` = `cr`.`id`
left join `accounts_contacts_1_c` as `ac1c` on `ac1c`.`accounts_ccb6contacts_idb` = `c`.`id`
left join `accounts` as `student_of` on `student_of`.`id` = `ac1c`.`accounts_cd3b9ccounts_ida`
left join `accounts_cstm` as `student_of_c` on `student_of_c`.`id_c` = `student_of`.`id`
left join `sw_bookings_accounts_c` as `ba` on `ba`.`sw_booking9ce7ookings_ida` = `b`.`id` and `ba`.`deleted` = ?
left join `accounts` as `booked_account` on `booked_account`.`id` = `ba`.`sw_booking6ef1ccounts_idb` and `booked_account`.`deleted` = ?
left join `accounts_cstm` as `booked_account_c` on `booked_account_c`.`id_c` = `booked_account`.`id`
where `b`.`booking_type` in (?, ?, ?, ?, ?)
and `b_cstm`.`booking_status_c` in (?)
and `c`.`deleted` = ?
and `cc`.`type_c` in (?, ?, ?, ?)
and (`b`.`booking_type` != ? or (`b`.`booking_type` = ?
and `b_cstm`.`invoice_party_c` = ?))
and LEAST(DATEDIFF(CURDATE(), c.date_modified), DATEDIFF(CURDATE(), b.date_modified)) <= ?
group by `c`.`id`
limit 10
# Welcome to SugarCRM!
с массивом связывания:
[0,0,0,0,"ACTIVE",0,"contact_type",0,"salutation",0,0,0,"RESEARCH","DISS","PREMED","MASTERS",
"SCHOOL","CONFIRMED",0,"Volunteer","Masters","Student","School","SCHOOL","SCHOOL","individual",28]
когда PDO::ATTR_EMULATE_PREPARES
имеет значение false, короткий запрос работает, а длинный запрос сбрасывает соединение с базой данных. когда PDO::ATTR_EMULATE_PREPARES
имеет значение true (по умолчанию), оба запроса работают правильно.
В более коротком запросе я попытался добавить переменные связывания, присоединения к еще нескольким таблицам, группировать, но он продолжает работать нормально. Где-то между ними есть точка или термин, который заставляет его терпеть неудачу.
Задача ещё не решена.
Других решений пока нет …