Как сделать так, чтобы предложение WHERE применялось только к правой таблице в левом соединении? - PullRequest
3 голосов
/ 27 марта 2012

У меня есть две таблицы.

TableA: field_definitions
field_id, field_type, field_length, field_name, field_desc, display_order, field_section, active

TableB: user_data
response_id, user_id, field_id, user_response

Мне нужен запрос, который будет возвращать все строки из таблицы A и, если они существуют, сопоставлять строки из таблицы B на основе определенного идентификатора пользователя.

Вот что у меня есть ...

SELECT field_definitions. * , user_data.user_response
FROM field_definitions
LEFT JOIN user_data
USING ( field_id ) 
WHERE (
user_data.user_id =8
OR user_data.user_id IS NULL
)
AND field_definitions.field_section =1
AND field_definitions.active =1
ORDER BY display_order ASC 

Это работает только в том случае, если таблица B имеет нулевые строки или совпадает с строками для user_id в предложении WHERE. Если в таблице B есть строки с совпадающим field_id, но не user_id, я получаю ноль возвращаемых строк.

По существу, если для пользователя X существуют строки в таблице B, запрос больше не возвращает строки из таблицы A при поиске ответов пользователя Z, и ни один из них не найден.

Мне нужно, чтобы результат всегда содержал строки из таблицы A, даже если в B нет подходящих строк с правильным user_id.

Ответы [ 4 ]

10 голосов
/ 27 марта 2012

Вы можете переместить эти ограничения из предложения WHERE в предложение ON (для этого сначала необходимо изменить предложение USING на предложение ON: предложения ON гораздо более гибкие, чем USING пункты). Итак:

SELECT field_definitions.*,
       user_data.user_response
  FROM field_definitions
  LEFT
  JOIN user_data
    ON user_data.field_id = field_definitions.field_id
   AND user_data.user_id = 8
 WHERE field_definitions.field_section = 1
   AND field_definitions.active = 1
 ORDER
    BY field_definitions.display_order ASC
;
3 голосов
/ 27 марта 2012

Концептуально сначала выполняется объединение, а затем к виртуальному набору результатов применяется предложение where.Если вы хотите сначала отфильтровать одну таблицу, вы должны закодировать ее в качестве дополнительного выбора внутри объединения.Что-то вроде этого:

SELECT 
  field_definitions. * , 
  user8.user_response
FROM 
  field_definitions
    LEFT JOIN (select * from user_data where user_id=8 or user_id is null) as user8
    USING ( field_id ) 
WHERE
  field_definitions.field_section =1
  AND field_definitions.active =1
ORDER BY display_order ASC 
1 голос
/ 27 марта 2012

Вы можете переместить предложение WHERE внутрь следующим образом

SELECT field_definitions. * , user_data.user_response
FROM (
  select * from
  field_definitions
  WHERE field_definitions.field_section =1
  AND field_definitions.active =1 ) as field_definitions
LEFT JOIN (
  select * from
  user_data
  where user_data.user_id =8
  OR user_data.user_id IS NULL ) as user_data
USING ( field_id ) 
ORDER BY display_order ASC 
0 голосов
/ 27 марта 2012

Дословный перевод sepc:

SELECT field_definitions. * , '{{MISSING}}' AS user_response
  FROM field_definitions 
UNION
SELECT field_definitions. * , user_data.user_response
  FROM field_definitions 
       NATURAL JOIN user_data
 WHERE user_data.user_id = 8;

Однако, я подозреваю, что вам не нужны "все строки из таблицы A".

...