SQL объединяет 3 таблицы с нулевыми значениями - PullRequest
1 голос
/ 11 июля 2019

У меня есть запрос, который возвращает участников, их последний визит и их последний платеж.Моя проблема в том, что он не возвращает участников без посещения и / или оплаты.

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

Я безуспешно пытался применить левое и правое соединения.Я также попытался добавить, например.«ИЛИ (pt.member_id IS NULL)» также безуспешно.

SELECT
    mr.member_id, 
    mr.name, 
    mr.tag, 
    pt.semester, 
    pt.date, 
    vt.date, 
FROM 
    members mr
INNER JOIN 
    payment pt 
ON 
    pt.member_id = mr.member_id 
    INNER JOIN 
        ( SELECT 
            member_id, 
            MAX(payment_id) max_value 
        FROM 
            payment 
        GROUP BY    
            member_id ) pt2 
    ON 
        pt.member_id = pt2.member_id 
    AND 
        pt.payment_id = pt2.max_value   
INNER JOIN 
    visit vt 
ON 
    vt.member_id = mr.member_id 
    INNER JOIN  
        ( SELECT    
            member_id, 
            MAX(date) max_visit_value 
        FROM 
            visit 
        GROUP BY 
            member_id ) vt2 
    ON 
        vt.member_id = vt2.member_id 
    AND 
        vt.date = vt2.max_visit_value

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

Надеюсь, что я понимаю, и кто-то может мне помочь:)

MySQL 5.6

Ответы [ 5 ]

0 голосов
/ 12 июля 2019

Небольшая настройка ответа Торстена Кеттнера заставила его работать:

Спасибо всем:)

SELECT
    mr.member_id,
    mr.name,
    mr.tag,
    pt.semester,
    pt.date,
    vt.date
FROM members mr

LEFT JOIN
(
    SELECT 
        member_id, 
        semester, 
        date        
    FROM payment
    WHERE ( member_id, date ) IN
    (
        SELECT 
            member_id, 
            MAX(date)
        FROM 
            payment
        GROUP BY 
            member_id
    )
 ) pt ON pt.member_id = mr.member_id

 LEFT JOIN
 (
    SELECT
        member_id,
        date,
        door
    FROM visit
    WHERE ( member_id, date ) IN
    (
        SELECT 
            member_id, 
            MAX(date)
        FROM 
            visit
        GROUP BY 
            member_id
    )
 ) vt ON vt.member_id = mr.member_id
0 голосов
/ 11 июля 2019

LEFT JOIN может помочь, если в основной таблице больше записей, которые наверняка дадут вам нулевые посещения / платежи, поскольку этих идентификаторов не будет в другой объединенной таблице. Если это не так, попробуйте выполнить отладку с помощью отдельных подзапросов. Проверьте, не обнаружены ли какие-либо нулевые значения для посещений / платежей при отдельном запуске.

0 голосов
/ 11 июля 2019

Коррелированные подзапросы могут быть простейшим решением:

SELECT mr.member_id, mr.name, mr.tag, 
       (SELECT pt.semester
        FROM payment pt
        WHERE pt.member_id = mr.member_id
        ORDER BY pt.date DESC
        LIMIT 1
       ) as last_payment_semester
       (SELECT pt.date
        FROM payment pt
        WHERE pt.member_id = mr.member_id
        ORDER BY pt.date DESC
        LIMIT 1
       ) as last_payment_date
       (SELECT MAX(vt.date,)
        FROM visit vt
        WHERE vt.member_id = mr.member_id
        ORDER BY vt.date DESC
        LIMIT 1
       ) as last_visit_date
FROM members mr;

Для производительности вам нужны индексы на payment(member_id, date desc, semester) и visit(member_id, date desc).

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

В MySQL 8+ вы можете использовать оконные функции:

SELECT mr.member_id, mr.name, mr.tag, pt.semester, pt.date, vt.date
FROM members mr LEFT JOIN
     (SELECT pt.*,
             ROW_NUMBER() OVER (PARTITION BY pt.member_id ORDER BY pt.date DESC) as seqnum
      FROM payment pt
     ) pt
     ON pt.member_id = mr.member_id AND pt.seqnum = 1 LEFT JOIN
     (SELECT pt.*,
             ROW_NUMBER() OVER (PARTITION BY vt.member_id ORDER BY vt.date DESC) as seqnum
      FROM visit vt
     ) vt
     ON vt.member_id = mr.member_id AND vt.seqnum = 1
0 голосов
/ 11 июля 2019

Самая большая проблема здесь в том, что член может иметь много платежей и много посещений.Вы только хотите показать последние из каждого.Это легко для посещений, так как вы хотите показать только (максимальную) дату.Для платежей, однако, вы также хотите показать семестр, относящийся к максимальной дате.Если семестр возрастает, как дата, то снова легко: используйте MAX(semester).Если это не так, тогда вы должны извлечь максимальную дату строка .

Начиная с MySQL 8:

SELECT
  mr.member_id, 
  mr.name, 
  mr.tag, 
  pt.semester, 
  pt.date, 
  vt.date 
FROM members mr
LEFT JOIN 
(
  SELECT
    member_id,
    semester,
    date,
    MAX(date) OVER (PARTITION BY member_id) AS last_date
  FROM payment
) pt ON pt.member_id = mr.member_id AND pt.dateb = pt.last_date
LEFT JOIN
(
  SELECT    
    member_id, 
    MAX(date) AS max_visit_value 
  FROM visit 
  GROUP BY member_id
) vt ON vt.member_id = mr.member_id 
ORDER BY mr.member_id;

В более ранних версиях:

SELECT
  mr.member_id, 
  mr.name, 
  mr.tag, 
  pt.semester, 
  pt.date, 
  vt.date 
FROM members mr
LEFT JOIN 
(
  SELECT
    member_id,
    semester,
    date
  FROM payment
  WHERE (member_id, date) IN 
  (
    SELECT member_id, MAX(date)
    FROM payment
    GROUP BY member_id
  )
) pt ON pt.member_id = mr.member_id
LEFT JOIN
(
  SELECT    
    member_id, 
    MAX(date) AS max_visit_value 
  FROM visit 
  GROUP BY member_id
) vt ON vt.member_id = mr.member_id 
ORDER BY mr.member_id;
0 голосов
/ 11 июля 2019

Следующая версия, использующая левые объединения везде, может дать желаемый результат:

SELECT
    mr.member_id,
    mr.name,
    mr.tag,
    pt.semester,
    pt.date,
    vt.date,
FROM members mr
LEFT JOIN payment pt
    ON pt.member_id = mr.member_id
LEFT JOIN
(
    SELECT member_id, MAX(payment_id) max_value
    FROM payment
    GROUP BY member_id
) pt2
    ON pt.member_id = pt2.member_id AND pt.payment_id = pt2.max_value
LEFT JOIN visit vt
    ON vt.member_id = mr.member_id
LEFT JOIN
(
    SELECT member_id, MAX(date) max_visit_value
    FROM visit
    GROUP BY member_id
) vt2
    ON vt.member_id = vt2.member_id AND vt.date = vt2.max_visit_value;
...