Почему пробелы влияют на MySQL Left Join и возвращают значения NULL? - PullRequest
0 голосов
/ 29 декабря 2011

У меня странная проблема, и мне нужна помощь в определении причины.До сих пор я мог наблюдать только симптомы.«Кажется» происходит то, что MySQL выполняет запрос иначе, в зависимости от того, есть ли у меня символ новой строки перед моим первым «LEFT JOIN».

Вот мои настройки:

Я выполняю запросы к экземпляру базы данных MySQL, предоставленному www.xeround.com.Я использую фреймворк Kohana, версия 3.0.Я использую PHP 5.3.3 FastCGI на Lighttpd.Я скомпилировал PHP с собственным драйвером MySQL:

- with-mysql = mysqlnd --with-mysqli = mysqlnd --with-pdo-mysql = mysqlnd

Я использую Kohana по умолчанию3 MySQL Database Driver и QueryBuilder.

Я собираюсь пропустить PHP-код и поделиться SQL, так как это кажется проблемой.QueryBuilder завершает построение этого запроса:

SELECT `fieldreps`.`user_id` AS `fieldreps.user_id`, `fieldreps`.`availability_id` AS `fieldreps.availability_id`, `fieldreps`.`applicant_type` AS `fieldreps.applicant_type`, `fieldreps`.`license_number` AS `fieldreps.license_number`, `fieldreps`.`license_exp` AS `fieldreps.license_exp`, `fieldreps`.`license_state` AS `fieldreps.license_state`, `fieldreps`.`car` AS `fieldreps.car`, `fieldreps`.`authorized_worker` AS `fieldreps.authorized_worker`, `fieldreps`.`restrictions` AS `fieldreps.restrictions`, `fieldreps`.`night` AS `fieldreps.night`, `fieldreps`.`day` AS `fieldreps.day`, `fieldreps`.`longer` AS `fieldreps.longer`, `fieldreps`.`commitment` AS `fieldreps.commitment`, `fieldreps`.`travel_metro` AS `fieldreps.travel_metro`, `fieldreps`.`travel_states` AS `fieldreps.travel_states`, `fieldreps`.`big_employee` AS `fieldreps.big_employee`, `fieldreps`.`employed` AS `fieldreps.employed`, `fieldreps`.`retail` AS `fieldreps.retail`, `fieldreps`.`status` AS `fieldreps.status`, `fieldreps`.`start` AS `fieldreps.start`, `fieldreps`.`sales` AS `fieldreps.sales`, `fieldreps`.`study` AS `fieldreps.study`, `fieldreps`.`relevant` AS `fieldreps.relevant`, `fieldreps`.`experience` AS `fieldreps.experience`, `fieldreps`.`claims` AS `fieldreps.claims`, `fieldreps`.`education` AS `fieldreps.education`, `fieldreps`.`degree_details` AS `fieldreps.degree_details`, `fieldreps`.`degree_institution` AS `fieldreps.degree_institution`, `fieldreps`.`other_training` AS `fieldreps.other_training`, `fieldreps`.`jobs` AS `fieldreps.jobs`, `fieldreps`.`current_training` AS `fieldreps.current_training`, `fieldreps`.`interested` AS `fieldreps.interested`, `fieldreps`.`achievements` AS `fieldreps.achievements`, `fieldreps`.`passions` AS `fieldreps.passions`, `fieldreps`.`ambitions` AS `fieldreps.ambitions`, `fieldreps`.`max_travel_time` AS `fieldreps.max_travel_time`, `fieldreps`.`creation_time` AS `fieldreps.creation_time`, `fieldreps`.`resume` AS `fieldreps.resume`, `users`.`user_id` AS `users.user_id`, `users`.`email` AS `users.email`, `users`.`name` AS `users.name`, `users`.`password` AS `users.password`, `users`.`given_name` AS `users.given_name`, `users`.`title` AS `users.title`, `users`.`nationality` AS `users.nationality`, `availabilities`.`availability_id` AS `availabilities.availability_id`, `availabilities`.`fieldrep_id` AS `availabilities.fieldrep_id`, `availabilities`.`mon_start` AS `availabilities.mon_start`, `availabilities`.`mon_end` AS `availabilities.mon_end`, `availabilities`.`tue_start` AS `availabilities.tue_start`, `availabilities`.`tue_end` AS `availabilities.tue_end`, `availabilities`.`wed_start` AS `availabilities.wed_start`, `availabilities`.`wed_end` AS `availabilities.wed_end`, `availabilities`.`thur_start` AS `availabilities.thur_start`, `availabilities`.`thur_end` AS `availabilities.thur_end`, `availabilities`.`fri_start` AS `availabilities.fri_start`, `availabilities`.`fri_end` AS `availabilities.fri_end`, `availabilities`.`sat_start` AS `availabilities.sat_start`, `availabilities`.`sat_end` AS `availabilities.sat_end`, `availabilities`.`sun_start` AS `availabilities.sun_start`, `availabilities`.`sun_end` AS `availabilities.sun_end` FROM `fieldreps` AS `fieldreps` LEFT JOIN `users` ON (`fieldreps`.`user_id` = `users`.`user_id`) LEFT JOIN `availabilities` ON (`fieldreps`.`availability_id` = `availabilities`.`availability_id`)

, который возвращает результат, подобный следующему: (Я удалил большинство полей, возвращаемых из таблицы Fieldreps, чтобы сделать ее короткой и менее личной.)

[0] => Array
    (
        [fieldreps.user_id] => 1f01f4c2-43fd-550d-a53d-1f191786ebad
        [fieldreps.availability_id] => e31b0773-ecba-41d1-8ebb-7ac718496456
        [fieldreps.car] => Yes
        [fieldreps.authorized_worker] => Yes
        [fieldreps.restrictions] => Has Restrictions: No 
        [fieldreps.night] => Yes
        [fieldreps.day] => Yes
        [fieldreps.longer] => Yes
        [users.user_id] => 1f01f4c2-43fd-550d-a53d-1f191786ebad
        [users.email] => dsfsdfsdfsdfsdfs@sdfsdfsdfsdfsd.com
        [users.name] => Jones
        [users.password] =>
        [users.given_name] => Fred
        [users.title] => Miss
        [users.nationality] => 
        [availabilities.availability_id] => 
        [availabilities.fieldrep_id] => 
        [availabilities.mon_start] => 
        [availabilities.mon_end] => 
        [availabilities.tue_start] => 
        [availabilities.tue_end] => 
        [availabilities.wed_start] => 
        [availabilities.wed_end] => 
        [availabilities.thur_start] => 
        [availabilities.thur_end] => 
        [availabilities.fri_start] => 
        [availabilities.fri_end] => 
        [availabilities.sat_start] => 
        [availabilities.sat_end] => 
        [availabilities.sun_start] => 
        [availabilities.sun_end] => 
    )
[1] => Array
    (
        [fieldreps.user_id] => 812c3a9f-d7d8-565a-a886-1b182753dd41
        [fieldreps.availability_id] => 
        [fieldreps.car] => 1
        [fieldreps.authorized_worker] => 1
        [fieldreps.restrictions] => 
        [fieldreps.night] => 1
        [fieldreps.day] => 1
        [fieldreps.longer] => 1
        [users.user_id] => 812c3a9f-d7d8-565a-a886-1b182753dd41
        [users.email] => sdfsdfsdfsdfsdf@sdfsdfsdfsdf.com
        [users.name] => Smith
        [users.password] => 
        [users.given_name] => Jill 
        [users.title] => 
        [users.nationality] => 
        [availabilities.availability_id] => 
        [availabilities.fieldrep_id] => 
        [availabilities.mon_start] => 
        [availabilities.mon_end] => 
        [availabilities.tue_start] => 
        [availabilities.tue_end] => 
        [availabilities.wed_start] => 
        [availabilities.wed_end] => 
        [availabilities.thur_start] => 
        [availabilities.thur_end] => 
        [availabilities.fri_start] => 
        [availabilities.fri_end] => 
        [availabilities.sat_start] => 
        [availabilities.sat_end] => 
        [availabilities.sun_start] => 
        [availabilities.sun_end] => 

Однако, когда я вставляю символ новой строки непосредственно перед первым LEFT JOIN, вот так:

SELECT `fieldreps`.`user_id` AS `fieldreps.user_id`, `fieldreps`.`availability_id` AS `fieldreps.availability_id`, `fieldreps`.`applicant_type` AS `fieldreps.applicant_type`, `fieldreps`.`license_number` AS `fieldreps.license_number`, `fieldreps`.`license_exp` AS `fieldreps.license_exp`, `fieldreps`.`license_state` AS `fieldreps.license_state`, `fieldreps`.`car` AS `fieldreps.car`, `fieldreps`.`authorized_worker` AS `fieldreps.authorized_worker`, `fieldreps`.`restrictions` AS `fieldreps.restrictions`, `fieldreps`.`night` AS `fieldreps.night`, `fieldreps`.`day` AS `fieldreps.day`, `fieldreps`.`longer` AS `fieldreps.longer`, `fieldreps`.`commitment` AS `fieldreps.commitment`, `fieldreps`.`travel_metro` AS `fieldreps.travel_metro`, `fieldreps`.`travel_states` AS `fieldreps.travel_states`, `fieldreps`.`big_employee` AS `fieldreps.big_employee`, `fieldreps`.`employed` AS `fieldreps.employed`, `fieldreps`.`retail` AS `fieldreps.retail`, `fieldreps`.`status` AS `fieldreps.status`, `fieldreps`.`start` AS `fieldreps.start`, `fieldreps`.`sales` AS `fieldreps.sales`, `fieldreps`.`study` AS `fieldreps.study`, `fieldreps`.`relevant` AS `fieldreps.relevant`, `fieldreps`.`experience` AS `fieldreps.experience`, `fieldreps`.`claims` AS `fieldreps.claims`, `fieldreps`.`education` AS `fieldreps.education`, `fieldreps`.`degree_details` AS `fieldreps.degree_details`, `fieldreps`.`degree_institution` AS `fieldreps.degree_institution`, `fieldreps`.`other_training` AS `fieldreps.other_training`, `fieldreps`.`jobs` AS `fieldreps.jobs`, `fieldreps`.`current_training` AS `fieldreps.current_training`, `fieldreps`.`interested` AS `fieldreps.interested`, `fieldreps`.`achievements` AS `fieldreps.achievements`, `fieldreps`.`passions` AS `fieldreps.passions`, `fieldreps`.`ambitions` AS `fieldreps.ambitions`, `fieldreps`.`max_travel_time` AS `fieldreps.max_travel_time`, `fieldreps`.`creation_time` AS `fieldreps.creation_time`, `fieldreps`.`resume` AS `fieldreps.resume`, `users`.`user_id` AS `users.user_id`, `users`.`email` AS `users.email`, `users`.`name` AS `users.name`, `users`.`password` AS `users.password`, `users`.`given_name` AS `users.given_name`, `users`.`title` AS `users.title`, `users`.`nationality` AS `users.nationality`, `availabilities`.`availability_id` AS `availabilities.availability_id`, `availabilities`.`fieldrep_id` AS `availabilities.fieldrep_id`, `availabilities`.`mon_start` AS `availabilities.mon_start`, `availabilities`.`mon_end` AS `availabilities.mon_end`, `availabilities`.`tue_start` AS `availabilities.tue_start`, `availabilities`.`tue_end` AS `availabilities.tue_end`, `availabilities`.`wed_start` AS `availabilities.wed_start`, `availabilities`.`wed_end` AS `availabilities.wed_end`, `availabilities`.`thur_start` AS `availabilities.thur_start`, `availabilities`.`thur_end` AS `availabilities.thur_end`, `availabilities`.`fri_start` AS `availabilities.fri_start`, `availabilities`.`fri_end` AS `availabilities.fri_end`, `availabilities`.`sat_start` AS `availabilities.sat_start`, `availabilities`.`sat_end` AS `availabilities.sat_end`, `availabilities`.`sun_start` AS `availabilities.sun_start`, `availabilities`.`sun_end` AS `availabilities.sun_end` FROM `fieldreps` AS `fieldreps` 
LEFT JOIN `users` ON (`fieldreps`.`user_id` = `users`.`user_id`) LEFT JOIN `availabilities` ON (`fieldreps`.`availability_id` = `availabilities`.`availability_id`)

Теперь я получаю желаемый результат:

[0] => Array
    (
        [fieldreps.user_id] => 1f01f4c2-43fd-550d-a53d-1f191786ebad
        [fieldreps.availability_id] => e31b0773-ecba-41d1-8ebb-7ac718496456
        [fieldreps.car] => Yes
        [fieldreps.authorized_worker] => Yes
        [fieldreps.restrictions] => Has Restrictions: No 
        [fieldreps.night] => Yes
        [fieldreps.day] => Yes
        [fieldreps.longer] => Yes
        [users.user_id] => 1f01f4c2-43fd-550d-a53d-1f191786ebad
        [users.email] => dsfsdfsdfsdfsdfs@sdfsdfsdfsdfsd.com
        [users.name] => Jones
        [users.password] =>
        [users.given_name] => Fred
        [users.title] => Miss
        [users.nationality] => 
        [availabilities.availability_id] => e31b0773-ecba-41d1-8ebb-7ac718496456
        [availabilities.fieldrep_id] => 1f01f4c2-43fd-550d-a53d-1f191786ebad
        [availabilities.mon_start] => 540
        [availabilities.mon_end] => 1020
        [availabilities.tue_start] => 540
        [availabilities.tue_end] => 1020
        [availabilities.wed_start] => 540
        [availabilities.wed_end] => 1020
        [availabilities.thur_start] => 540
        [availabilities.thur_end] => 1020
        [availabilities.fri_start] => 540
        [availabilities.fri_end] => 1020
        [availabilities.sat_start] => 
        [availabilities.sat_end] => 
        [availabilities.sun_start] => 
        [availabilities.sun_end] => 
    )
[1] => Array
    (
        [fieldreps.user_id] => 812c3a9f-d7d8-565a-a886-1b182753dd41
        [fieldreps.availability_id] => 
        [fieldreps.car] => 1
        [fieldreps.authorized_worker] => 1
        [fieldreps.restrictions] => 
        [fieldreps.night] => 1
        [fieldreps.day] => 1
        [fieldreps.longer] => 1
        [users.user_id] => 812c3a9f-d7d8-565a-a886-1b182753dd41
        [users.email] => sdfsdfsdfsdfsdf@sdfsdfsdfsdf.com
        [users.name] => Smith
        [users.password] => 
        [users.given_name] => Jill 
        [users.title] => 
        [users.nationality] => 
        [availabilities.availability_id] => 
        [availabilities.fieldrep_id] => 
        [availabilities.mon_start] => 
        [availabilities.mon_end] => 
        [availabilities.tue_start] => 
        [availabilities.tue_end] => 
        [availabilities.wed_start] => 
        [availabilities.wed_end] => 
        [availabilities.thur_start] => 
        [availabilities.thur_end] => 
        [availabilities.fri_start] => 
        [availabilities.fri_end] => 
        [availabilities.sat_start] => 
        [availabilities.sat_end] => 
        [availabilities.sun_start] => 
        [availabilities.sun_end] => 

Обратите внимание, чтоСЛЕДУЮЩЕЕ СОЕДИНЕНИЕ по таблице «Пользователи» прекрасно работает в обоих случаях.Я также попытался добавить символ новой строки перед вторым LEFT JOIN вместо первого.Тот же самый нежелательный результат.Единственное, что я могу найти, чтобы это исправить - это поместить новую строку перед первым левым соединением.Запросы, которые создаются QueryBuilder и отправляются в MySQL, всегда длинные, однострочные, минимальные пробельные строки ... никаких новых строк!Это первый раз, когда у меня возникла эта проблема.Я просто не могу поверить, что перед первым LEFT JOIN возникнет необходимость поместить новую строку.

Мой вопрос сводится к следующим пунктам:

  1. Должно ли это происходить так?путь?(Пробелы, влияющие на результат запроса)
  2. Как лучше всего получить желаемый результат?

Я не эксперт по SQL, иначе мне, вероятно, не понадобилось бызадать этот вопросЯ уверен, что есть кое-что, что я пропускаю.

Спасибо,

Джонатан

1 Ответ

0 голосов
/ 30 декабря 2011

Моя проблема была прямо передо мной, как я и думал:

Индексы

Я случайно создал два индекса доступности. Fieldrep_id, а индексы для остальной схемы были, в лучшем случае, дрянными.

Я использовал EXPLAIN, который указал, что он не использует индекс для 2 из трех соединений. Я почистил свои индексы, и теперь он отлично работает каждый раз.

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

Несмотря на это, проблема была с индексами. Все остальное было просто симптомом.

Может быть, это поможет кому-то сэкономить немного времени.

...