MySQL коррелированный подзапрос в синтаксисе JOIN - PullRequest
18 голосов
/ 10 февраля 2009

Я хотел бы предоставить условие WHERE для внутреннего запроса, указав innertable.id = outertable.id. Однако MySQL (5.0.45) сообщает «Неизвестный столбец« externaltable.id »в« предложении where »». Возможен ли этот тип запроса?

Внутренним запросом является поворот строк в столбцы с использованием GROUP BY. Это может быть полностью выполнено во внешнем запросе, но может повлечь дополнительные издержки из-за дополнительных объединений.

В качестве альтернативы, я могу пропустить условие WHERE во внутреннем запросе и вместо этого указать ON externaltable.id = innerquery.id, но тогда он получит весь набор строк внутреннего запроса, чтобы снова присоединиться к внешнему, что неэффективно.

Фактический SQL отображается ниже:

select t.ticketid, u.userid, t.fullname, u.loginapi_userid, t.email, tp.subject, tp.contents, a.PhoneNumber, a.Location, a.Extension, a.BusinessUnit, a.Department
from swtickets t
inner join swticketposts tp on t.ticketid = tp.ticketid
inner join swusers u on t.userid = u.userid
left join
  (
  select
  cfv.typeid,
  min(case cfv.customfieldid when 1 then cfv.fieldvalue end) as 'PhoneNumber',
  min(case cfv.customfieldid when 3 then cfv.fieldvalue end) as 'Location',
  min(case cfv.customfieldid when 5 then cfv.fieldvalue end) as 'Extension',
  min(case cfv.customfieldid when 8 then cfv.fieldvalue end) as 'BusinessUnit',
  min(case cfv.customfieldid when 9 then cfv.fieldvalue end) as 'Department'
  from swcustomfieldvalues cfv
  where cfv.typeid = t.ticketid
  group by cfv.typeid
  ) as a on 1 = 1
where t.ticketid = 2458;

Ответы [ 5 ]

30 голосов
/ 12 февраля 2009

Ответ на ваш вопрос - нет, невозможно ссылаться на имена корреляций, как вы делаете. Производная таблица создается вашим внутренним запросом до того, как внешний запрос начнет оценивать соединения. Поэтому имена корреляции, такие как t, tp и u, недоступны для внутреннего запроса.

Чтобы решить эту проблему, я рекомендую использовать то же самое целочисленное значение константы во внутреннем запросе, а затем присоединить производную таблицу во внешнем запросе, используя реальное условие вместо 1=1.

SELECT t.ticketid, u.userid, t.fullname, u.loginapi_userid, t.email,
  tp.subject, tp.contents, a.PhoneNumber, a.Location, a.Extension,
  a.BusinessUnit, a.Department
FROM swtickets t
 INNER JOIN swticketposts tp ON (t.ticketid = tp.ticketid)
 INNER JOIN swusers u ON (t.userid = u.userid)
 LEFT OUTER JOIN (
  SELECT cfv.typeid,
    MIN(CASE cfv.customfieldid WHEN 1 THEN cfv.fieldvalue END) AS 'PhoneNumber',
    MIN(CASE cfv.customfieldid WHEN 3 THEN cfv.fieldvalue END) AS 'Location',
    MIN(CASE cfv.customfieldid WHEN 5 THEN cfv.fieldvalue END) AS 'Extension',
    MIN(CASE cfv.customfieldid WHEN 8 THEN cfv.fieldvalue END) AS 'BusinessUnit',
    MIN(CASE cfv.customfieldid WHEN 9 THEN cfv.fieldvalue END) AS 'Department'
  FROM swcustomfieldvalues cfv
  WHERE cfv.typeid = 2458
  GROUP BY cfv.typeid
  ) AS a ON (a.typeid = t.ticketid)
WHERE t.ticketid = 2458;
2 голосов
/ 10 февраля 2009

Вы используете дизайн Entity-Attribute-Value, и в конечном итоге нет способа сделать это масштабируемым, если вы попытаетесь генерировать обычные наборы результатов. Не пытайтесь сделать это одним запросом.

Вместо этого сначала запросите ваши нормализованные таблицы:

SELECT t.ticketid, u.userid, t.fullname, u.loginapi_userid, t.email, 
  tp.subject, tp.contents
FROM swtickets t
 INNER JOIN swticketposts tp ON (t.ticketid = tp.ticketid)
 INNER JOIN swusers u ON (t.userid = u.userid)
WHERE t.ticketid = 2458;

Затем запросите ваши пользовательские поля с результатом в нескольких строках набора результатов:

SELECT cfv.customfieldid, cfv.fieldvalue
FROM swcustomfieldvalues cfv
WHERE cfv.typeid = 2458;

Вы получите несколько строк в наборе результатов, по одной строке для каждого настраиваемого поля:

+---------------+--------------+
| customfieldid | fieldvalue   |
+---------------+--------------+
|             1 | 415-555-1234 |
|             3 | Third office |
|             5 | 123          |
|             8 | Support      |
|             9 | Engineering  |
+---------------+--------------+

Затем вам нужно написать код приложения для сопоставления полей набора результатов с полями объекта приложения в цикле.

Использование таблицы Entity-Attribute-Value таким способом является более масштабируемым как с точки зрения производительности, так и с точки зрения обслуживания кода.

1 голос
/ 10 февраля 2009

Я бы написал это с несколькими объединениями. Когда вы говорите, что «это может повлечь за собой дополнительные издержки», это говорит мне, что вы не проверяли это, чтобы быть уверенным. Если у вас есть приличные индексы, объединения должны быть довольно тривиальными.

Здесь также показана одна из ловушек универсального шаблона оформления таблицы «все держать».

0 голосов
/ 10 февраля 2009

Я полагаю, проблема в 'cfv.typeid = t.ticketid'? Я думаю об этом так: хотя MySQL поддерживает коррелированные подзапросы, то, что вы пытаетесь сделать, похоже, может потерпеть неудачу в соединении, потому что «внутренний» запрос на самом деле не «внутри» остальной части запроса, как он в предложении WHERE. Но похоже, что вы могли бы просто взять предложение where из подзапроса и создать условие соединения для a.typeid = t.ticketid.

0 голосов
/ 10 февраля 2009

Мое предложение должно было быть тем, что вы исключили из соображений эффективности. Например. опуская предложение where и используя соединение (согласно t.ticketid = a.ticketid)

Удалось ли вам доказать свои мысли о неэффективности на конкретных примерах? Я знаю, что вы говорите, но какой бы метод вы ни использовали, каждую строку во внешнем запросе объединяют с каждой строкой во внутреннем запросе, поэтому в зависимости от плана выполнения он может быть не таким неэффективным, как вы подозреваете?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...