MYSQL JOIN дает неожиданные (но приятные) результаты? - PullRequest
5 голосов
/ 17 февраля 2012

Эй, ребята, я ломаю голову над этим, я надеюсь, что кто-то может объяснить это для мне. Честно говоря, я немного боюсь, что это может укусить меня в задницу позже ...

Итак, у меня есть три стола, из которых я сейчас тяну. reports, berries и melons. Я настроил свой запрос следующим образом, и он получил именно то, что я хочу.

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label

FROM reports rpt

LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key

WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

Я получаю ожидаемое возвращение, которое

key | role | region   | inspection_type | inspection_number | shipper   | po    | commodity     | label
3   | NULL | Seattle  | melons          | 5555              | Shipper1  | PO2   | Commodity2    | Label2
2   | NULL | Seattle  | berries         | 1023              | Shipper1  | PO1   | Commodity1    | Label1

Если я удалю LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key из своего утверждения, то получу точно такую ​​же вещь ... Я никогда не упоминал melons ??

Если я пересмотрю и использую JOIN вместо ягод

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label

FROM reports rpt

JOIN berries ber ON rpt.inspection_number = ber.report_key

WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

Он производит то, что я ожидал, должен!

key | role | region   | inspection_type | inspection_number | shipper   | po    | commodity     | label
2   | NULL | Seattle  | berries         | 1023              | Shipper1  | PO1   | Commodity1    | Label1

Но пытаюсь пересмотреть мою инструкцию SQL следующим образом ...

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label
   mel.shipper, mel.po, mel.commodity, mel.label

FROM reports rpt

JOIN berries ber ON rpt.inspection_number = ber.report_key
JOIN melons mel ON rpt.inspection_number = mel.report_key

WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

Сетей мне ....

MySQL returned an empty result set (i.e. zero rows). ( Query took 0.0011 sec ) Дает мне большой средний палец. Что за черт? Может кто-нибудь объяснить, что я явно делаю неправильно и как это исправить?

Ответы [ 3 ]

5 голосов
/ 17 февраля 2012

Это не так сложно.Ваш первый запрос, вы присоединяетесь к mel, но никогда ничего не делаете с ним, поэтому вы только получаете данные Бер.Ваш последний запрос ближе, но поскольку вы внутренне объединяетесь с ягодами и дынями и у вас нет отчетов, которые оба , вы не получите результатов.Но ответ ближе к тому, что вы делаете во втором запросе, и я думаю, что вы хотите, это:

SELECT 
   rpt.*, 
   COALESCE(ber.shipper, mel.shipper) AS shipper,
   COALESCE(ber.po, mel.po) AS po, 
   COALESCE(ber.commodity, mel.commodity) AS commodity,
   COALESCE(ber.label, mel.label) AS label
FROM reports rpt
LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key
WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

Этот запрос говорит, дать мне строки, где есть соединение ягод или дынь,но для столбцов, которые у них общие, дайте мне, какой бы он ни был.Сначала мы берем ber без особой причины.

Предполагая, что эти две таблицы взаимоисключающие, я думаю, что это то, что вы хотите.

Редактировать : построение на@MarcusAdams указывает на то, что это можно переписать, чтобы использовать UNION, если существует отвратительное количество таблиц фруктов:

SELECT report_key, shipper, po, commodity, label FROM berries
UNION
SELECT report_key, shipper, po, commodity, label FROM melons
UNION
SELECT report_key, shipper, po, commodity, label FROM ...
...

Этот запрос даст вам что-то удобное, что вы можете использовать как подзапросвид) позже.Вы также можете жестко закодировать имя источника следующим образом:

SELECT report_key, shipper, po, commodity, label, 'berries' AS type FROM berries
UNION
SELECT report_key, shipper, po, commodity, label, 'melons' FROM melons
UNION
SELECT report_key, shipper, po, commodity, label, '...' FROM ...
...

Затем, чтобы использовать это в своем исходном запросе, вы должны встроить его так:

SELECT *
FROM reports rpt,
JOIN (SELECT report_key, shipper, po, commodity, label, 'berries' AS type FROM berries
      UNION
      SELECT report_key, shipper, po, commodity, label, 'melons' FROM melons
      UNION
      SELECT report_key, shipper, po, commodity, label, '...' FROM ...
      ...) fruits ON rpt.inspection_number = fruits.report_key
WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC
3 голосов
/ 18 февраля 2012

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

ВотЗапрос Даниэля:

SELECT 
   rpt.*, 
   COALESCE(ber.shipper, mel.shipper) AS shipper,
   COALESCE(ber.po, mel.po) AS po, 
   COALESCE(ber.commodity, mel.commodity) AS commodity,
   COALESCE(ber.label, mel.label) AS label
FROM reports rpt
LEFT JOIN berries ber ON rpt.inspection_number = ber.report_key
LEFT JOIN melons mel ON rpt.inspection_number = mel.report_key
WHERE rpt.status='1'  OR rpt.status='0'
ORDER BY rpt.inspection_number DESC

Этот запрос прекрасно работает, и только для двух фруктов он довольно оптимизирован.Тем не менее, поскольку отчеты являются взаимоисключающими, запрос пытается выполнить дополнительное объединение, чем это необходимо.Например, если запись отчета уже присоединена к записи ягоды, мы знаем, что она не будет присоединена к записи дыни, но MySQL этого не знает.Вместо этого MySQL выполнит еще один поиск, чтобы попытаться выполнить соединение с таблицей дыни, даже если соответствующая запись не будет найдена.

Только с двумя объединениями половина попыток объединения теряется.Однако при трех фруктах две трети попыток объединения тратятся впустую, при четырех фруктах три четверти попыток объединения тратятся впустую и т. Д.

Чтобы избежать дополнительных попыток объединения, мы можем обратить вспятьпорядок соединений, например:

(SELECT rpt.*, ber.shipper, ber.po, ber.commondity, ber.label
FROM berries ber
JOIN reports rpt
  ON rpt.inspection_number = ber.report_key
WHERE rpt.status = '1' OR rpt.status = '0')
UNION ALL
(SELECT rpt.*, mel.shipper, mel.po, mel.commondity, mel.label
FROM melons mel
JOIN reports rpt
  ON rpt.inspection_number = mel.report_key
WHERE rpt.status = '1' OR rpt.status = '0')
ORDER BY inspection_number DESC

Здесь мы начинаем с другого направления (с фруктами) и возвращаемся к отчетам.Это позволяет нам делать только одно объединение на отчет.

Обратите внимание, что теперь мы используем INNER JOIN вместо LEFT JOIN для каждого фрукта, и мы используем UNION ALL, чтобы объединить результаты для каждого фрукта.фрукты в один больший набор результатов.

Для дальнейшей оптимизации иногда MySQL не распознает две константы, такие как 1 и 0, как диапазон, особенно если это не целочисленное поле.Поиск по диапазону выполняется быстрее, чем два отдельных поиска, поэтому для подсказки MySQL, что rpt.status для 1 и 0 является диапазоном, используйте BETWEEN вместо OR, предполагая, что у вас есть индекс покрытия (rpt.inspection_number, rpt.status).

0 голосов
/ 17 февраля 2012

Итак, прежде всего, вы заметите, что этот запрос SELECT не выбирает какие-либо данные из таблицы дынь. Поэтому вам не нужно присоединяться к таблице дынь, потому что вы не выбираете из нее никаких данных, и просто не имеет значения, что находится в ней или как вы на нее ссылаетесь, потому что вы ничего не выбираете из нее.

SELECT 
   rpt.*, 
   ber.shipper, ber.po, ber.commodity, ber.label

Далее, я не слишком знаком с различиями между LEFT JOIN, INNER JOIN и просто JOIN, поэтому я не могу ответить на этот вопрос. Однако, исходя из моего опыта работы с MySQL, попробуйте изменить последний запрос на INNER JOIN, а не JOIN, и посмотрите, работает ли он. На сайте MySQL имеется документация , касающаяся различий между различными типами объединений.

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

...