简体   繁体   English

SQL连接3个具有空值的表

[英]SQL Join 3 tables with null values

I have a query that returns members, their last visit and their last payment. 我有一个查询,该查询返回成员,他们的最后访问和最后的付款。 My problem is that it doesn't return members without a visit and/or a payment. 我的问题是,没有访问和/或付款,它不会返回会员。

I previously didnt include the last visits and I then had a query with LEFT and RIGHT JOINs instead of INNER but when I added the visit table I received som help to include it but we didn't notice that we were missing the members with null values in visit or payment. 我以前没有包括最后一次访问,然后我用LEFT和RIGHT JOINs而不是INNER进行了查询,但是当我添加访问表时,我得到了som help来包含它,但我们没有注意到我们缺少具有空值的成员造访或付款。

I've tried applying LEFT and RIGHT JOINs without any luck. 我试过应用左和右联接没有任何运气。 I've also tried adding eg. 我也尝试添加例如。 "OR (pt.member_id IS NULL)" also without success. “ OR(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

I want to get a result where visit and/or payment can be null. 我想得到一个访问和/或付款可以为空的结果。

I hope I make sense and that someone can help me :) 我希望我有道理,希望有人可以帮助我:)

MySQL 5.6 MySQL 5.6

The following version using left joins everywhere might give you the result you want: 以下版本在各处使用左联接可能会为您提供所需的结果:

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;

Correlated subqueries might be the simplest solution: 关联子查询可能是最简单的解决方案:

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;

For performance, you want indexes on payment(member_id, date desc, semester) and visit(member_id, date desc) . 为了提高性能,您需要在payment(member_id, date desc, semester)visit(member_id, date desc)上建立索引。

Admittedly, having to repeat essentially the same subquery twice for the date and the semester is somewhat inelegant. 诚然,在日期和学期中必须重复本质上相同的子查询两次,这有点麻烦。

In MySQL 8+, you can use window functions: 在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

The biggest problem here is that a member can have many payments and many visits. 这里最大的问题是会员可以进行很多付款和多次访问。 You only want to show the latest of each. 您只想显示每个最新的。 That is easy for the visits, as you only want to show the (maximum) date. 这对于访问很容易,因为您只想显示(最大)日期。 For payments, however, you also want to show the semester belonging to the maximum date. 但是,对于付款,您还想显示属于最长日期的学期。 If the semester is ascending like the date, then it's easy again: Use MAX(semester) . 如果学期像日期一样升序,那么这又很容易:使用MAX(semester) If it isn't, then you must retrieve the maximum date row instead. 如果不是,则必须检索最大日期

As of MySQL 8: 从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;

In earlier versions: 在早期版本中:

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;

LEFT JOIN can help if records are more in master table that would surely give you null visits/payments as those ids would not be there in the other joined table. 如果主表中的记录更多,那么LEFT JOIN可以提供帮助,因为这些ID在其他联接表中不会出现,因此肯定会给您空访问/付款。 If that isnt the case too try to debug via run subqueries seperately check for are there any null values encountered for visits/payments when run seperately. 如果不是这种情况,也请尝试通过运行子查询单独进行调试,以检查单独运行时是否存在访问/付款遇到的任何空值。

A little tweak on Thorsten Kettner's answer made it work: 稍微调整一下Thorsten Kettner的答案就可以了:

Thanks everyone :) 感谢大家 :)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM