Результаты из нескольких таблиц MySQL

#mysql #sql #join

#mysql #sql #Присоединиться

Вопрос:

Две таблицы

Таблица 1 — таблица клиентов

 user_id      Name
1            first
2            Second
  

Таблица 2 — таблица customer_activity

 user_id      type
1            downloaded_software
1            filled_download_form
2            downloaded_software
2            filled_download_form
2            purchased
  

Цель — выбрать всех клиентов, которые скачали_software, заполнили_download_form и приобрели.

Мой запрос

     SELECT SQL_CALC_FOUND_ROWS DISTINCT(c.user_id) 
      FROM customer AS c 
INNER JOIN customer_activity AS ca ON ca.user_id = c.user_id 
     WHERE ca.type IN('downloaded_software','filled_download_form','purchased') 
  ORDER BY c.user_id asc 
     LIMIT 0, 100
  

Результат

 1
2
  

Желаемый результат

 2
  

Редактировать:
Резюме комментариев:

Ответ на этот вопрос приведен ниже, но возможным хорошим сценарием было бы исключить некоторые элементы из списка. Например, если я хочу выполнить поиск клиентов, которые загрузили_software_ware, заполните_software_form, но не приобрели.

На это ответил @Serpiton в этой скрипке в комментариях.

Ответ №1:

Вы можете группировать user_id и подсчитывать различные значения type . Поскольку WHERE ограничивает значения только 3 возможными, количество различных значений должно быть 3, если все найдены;

 SELECT SQL_CALC_FOUND_ROWS c.user_id 
FROM customer AS c 
JOIN customer_activity AS ca ON ca.user_id = c.user_id 
WHERE ca.type IN('downloaded_software', 'filled_download_form', 'purchased') 
GROUP BY c.user_id
HAVING COUNT(DISTINCT ca.type) = 3
ORDER BY c.user_id 
LIMIT 0, 100
  

SQLFiddle для тестирования.

РЕДАКТИРОВАТЬ: чтобы ответить на ваш вопрос из комментариев, если вам нужно исключить тип, вы не можете легко использовать GROUP BY для поиска результатов. Вы можете либо выполнить самосоединение для каждого типа (слева присоединиться к исключенному и проверить, что это не приводит ни к одной строке);

 SELECT SQL_CALC_FOUND_ROWS c.user_id 
FROM customer AS c 
JOIN customer_activity AS ca1
  ON ca1.user_id = c.user_id AND ca1.type = 'downloaded_software'
JOIN customer_activity AS ca2
  ON ca2.user_id = c.user_id AND ca2.type = 'filled_download_form'
LEFT JOIN customer_activity AS ca3
  ON ca3.user_id = c.user_id AND ca3.type = 'purchased'
WHERE ca3.user_id IS NULL 
ORDER BY c.user_id 
LIMIT 0, 100
  

…или — не так эффективно, но, возможно, проще, если вы автоматически генерируете запрос — вы можете сделать это с помощью 3 простых подзапросов, используя IN и NOT IN , чтобы выбрать, следует ли включать тип или нет…

 SELECT SQL_CALC_FOUND_ROWS c.user_id 
FROM customer AS c
WHERE c.user_id IN (
  SELECT user_id FROM customer_activity WHERE type='downloaded_software'
) AND c.user_id IN (
  SELECT user_id FROM customer_activity WHERE type='filled_download_form'
) AND c.user_id NOT IN (
  SELECT user_id FROM customer_activity WHERE type='purchased'
) 
ORDER BY c.user_id
LIMIT 0,100;
  

SQLFiddle, показывающий оба в действии.

Комментарии:

1. Да, этот намного лучше.

2. Приятно!! Это работает. Что делать, если я хочу исключить тип… Так, например, я хочу ориентироваться на клиентов, которые загрузили_software, filled_download_form, но не приобрели.

3. @KaranKhanna добавил два возможных примера этого.

4. Можно исключить тип, изменив первый запрос HAVING на HAVING COUNT(DISTINCT ca.type) = 2 AND COUNT(CASE WHEN ca.type = 'purchased' THEN 1 ELSE NULL END) = 0 , тип в CASE является тем, который нужно исключить

5. @KaranKhanna может быть, я не понял, проверьте эту скрипку

Ответ №2:

Отказ от ответственности: в комментарии к ответу Йоахима Исакссона я предложил вариант одного из его запросов к OP, который попросил разъяснений, вот и все.

Начиная с запроса с изменением

 SELECT SQL_CALC_FOUND_ROWS c.user_id 
FROM customer AS c 
JOIN customer_activity AS ca 
  ON ca.user_id = c.user_id 
WHERE ca.type IN('downloaded_software',
                 'filled_download_form',
                 'purchased') 
GROUP BY c.user_id
HAVING COUNT(DISTINCT ca.type) = 2
   AND COUNT(CASE WHEN ca.type = 'purchased' THEN 1 ELSE NULL END) = 0
ORDER BY c.user_id 
LIMIT 0, 100
  

часть, выделенная жирным шрифтом, - это мое предлагаемое редактирование.

Если что-то неясно за пределами части, которую я изменил, вам следует обратиться к Йоахиму Исакссону, поскольку именно он написал запрос.

Мое редактирование делает то, что указано в tin: первое условие проверяет, что есть только два из трех допустимых значений type , второе проверяет, что "приобретено" - это то, что не указано. Второе условие эквивалентно

 SUM(CASE WHEN ca.type = 'purchased' THEN 1 ELSE 0 END) = 0
  

это, возможно, более просто для чтения.

Весь запрос эквивалентен

 SELECT SQL_CALC_FOUND_ROWS c.user_id 
FROM customer AS c 
JOIN customer_activity AS ca 
  ON ca.user_id = c.user_id 
WHERE ca.type IN('downloaded_software',
                 'filled_download_form',
                 'purchased') 
GROUP BY c.user_id
HAVING COUNT(DISTINCT 
             CASE WHEN ca.type = 'downloaded_software' THEN 1 ELSE NULL END) = 1
   AND COUNT(DISTINCT 
             CASE WHEN ca.type = 'filled_download_form' THEN 1 ELSE NULL END) = 1
   AND COUNT(DISTINCT 
             CASE WHEN ca.type = 'purchased' THEN 1 ELSE NULL END) = 0
ORDER BY c.user_id 
LIMIT 0, 100
  

(если у вас есть только эти 3 типа WHERE , это не обязательно)

Если вы пишете запрос с языка программирования, я буду использовать это в качестве шаблона

 SELECT SQL_CALC_FOUND_ROWS c.user_id 
FROM customer AS c 
JOIN customer_activity AS ca 
  ON ca.user_id = c.user_id 
WHERE ca.type IN('downloaded_software',
                 'filled_download_form',
                 'purchased') 
GROUP BY c.user_id
HAVING COUNT(DISTINCT 
             CASE WHEN ca.type = 'downloaded_software' 
                  THEN 1 ELSE NULL END) = ?downloaded?
   AND COUNT(DISTINCT 
             CASE WHEN ca.type = 'filled_download_form' 
                  THEN 1 ELSE NULL END) = ?filled?
   AND COUNT(DISTINCT 
             CASE WHEN ca.type = 'purchased' 
                  THEN 1 ELSE NULL END) = ?purchased?
ORDER BY c.user_id 
LIMIT 0, 100
  

с ?downloaded? ?filled? ?purchased? параметрами и в качестве. 1 означает, что тип должен присутствовать, 0 означает, что параметр должен отсутствовать

Например, чтобы ответить на другой вопрос, используйте только целевых клиентов, у которых нет ни filled_download_form, ни purchased. Каким будет запрос для этого?просто заполните параметры соответствующим образом.

Комментарии:

1. Большое спасибо, приятель, за хорошее объяснение. Также это решение не будет работать, если у меня просто есть 1 параметр отрицания, верно? Например, просто выберите клиентов, которые не приобрели сегодня?

2. Извините... база данных была слишком большой для отображения, поэтому я не написал в вопросе. Там есть столбец даты, но давайте оставим сегодняшнюю часть. Дело в том, что все это фильтры, которые может выбрать клиент. Они могут выбирать или не выбирать все. Итак, предположим, что если они выбирают только "не приобретено", то это не работает.