左/右连接不按预期返回空值

时间:2017-09-05 07:49:00

标签: sql mariadb

我正在使用MariaDB-10.2.6。

目前架构如下:

CREATE TABLE `daily_report` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `date` date DEFAULT NULL,
  `user_id` varchar(255) NOT NULL,
  CONSTRAINT `daily_report_user_id_fk` FOREIGN KEY (`user_id`) REFERENCES `user` 
  (`id`) ON DELETE SET NULL ON UPDATE CASCADE
)

CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `shall_create_every_day` tinyint(1) NOT NULL
)

CREATE TABLE `calendar` (
  `datefield` date
)

规则:

  1. shall_create_every_day设置为1的用户每天都会创建一个daily_report
  2. 我有一个日历表calendar,其中包含从2000-01-012020-12-31的每个日期(因为在MySQL / MariaDB中没有generate_series)
  3. 我尝试了以下查询:

    select daily_report.id, daily_report.date, user.id, calendar.datefield
    from calendar
    left join daily_report on daily_report.date=calendar.datefield
    right join user on daily_report.user_id=user.id and user.shall_create_every_day=1
    where calendar.datefield='2017-08-02'
    

    我正在尝试使用日期范围,但出于测试目的,我将where语句限制为仅限一天。

    它将返回shall_create_every_day的所有未在“2017-08-12”创建任何daily_report的用户。但是我当天只得到了创建报告的组合。

    有什么我误解左/右连接为不匹配的行返回空值?

4 个答案:

答案 0 :(得分:1)

MariaDB中提供了Generate系列。见here。它就像

SELECT '2000-01-01' + INTERVAL seq
    FROM seq_0_241

您可以JOIN这样做,而无需实际实现表格。

请不要混用LEFT JOINRIGHT JOIN。这或者是问题的原因,或者我的头脑太忙于了解它。请记住,它可能被解释为

FROM a LEFT JOIN (b RIGHT JOIN c)

这可能不是你想要的。在任何情况下,都允许使用括号(并在混合LEFTRIGHT时鼓励使用括号。)

答案 1 :(得分:0)

你以一种有点复杂的方式表达了你的疑问。我想通过将user表连接到第二个表来表达这一点,该表包含每个用户每天报告的条目。没有写入的用户的标志是,给定的用户记录与第二个表中的任何内容都不匹配。

select
    u.id
from user u
left join
(
    select
        c.datefield,
        dr.me_id
    from calendar c
    left join daily_report dr
        on dr.date = c.datefield
    where c.datefield = '2017-08-02'
 ) cal
    on cal.me_id = u.id and
       u.shall_create_every_day = 1
where
    cal.me_id is null;

答案 2 :(得分:0)

永远不要组合左右连接;这只是令人困惑。 (好吧,我们大多数人从来没有使用过正确的连接,因为它们更难以阅读然后离开连接。)

您的查询是这样做的:

  1. 获取所有日历记录。
  2. 加入daily_report记录(如果可用),否则创建一个空虚拟记录以加入。
  3. 使用shall_create_every_day = 1获取所有用户记录。
  4. 如果可用,将第一个数据集加入用户,否则创建一个空的虚拟记录以加入用户。
  5. 通过应用where子句来关闭在(4)中创建的所有外连接记录。
  6. 首先,您应该问问自己:我是否需要外部联接?关于用户和日历,你没有。实际上你想要的日历是检查“2017-08-02”的条目是否存在。至于用户表:这也仅仅是标准;您想要某些用户的所有报告和特定日期。

    因此,请从报告表中进行选择,并在其WHERE子句中包含其所属的条件。

    select id, date, user_id
    from daily_report 
    where user_id in (select id from user where shall_create_every_day = 1)
    and date = (select datefield from calendar where datefield = '20170802');
    

    要显示日期和用户及其报告(包括缺少报告),您需要交叉加入日历和用户,然后加入报告:

    select r.id, c.datefield, u.id as user_id
    from (select id from user where shall_create_every_day = 1) u
    cross join (select datefield from calendar where datefield = '20170802') c
    left join daily_report r on r.user_id = u.id and r.date = c.datefield;
    

答案 3 :(得分:0)

可以这样做。

daily_report.user_id

P.S。可能表结构中存在错误。 int应为varchar而不是 <!DOCTYPE html> <html> <head> <script src="jquery.js"></script> <script> $(document).ready(function(){ $('#a1').keyup(calculate); $('#a2').keyup(calculate); $('#a3').keyup(calculate); }); function calculate(e) { if ($('input.checkbox_check').is(':checked')) { $('#a4').val(($('#a1').val() * $('#a2').val())*0.6); $('#a3').val($('#a1').val() * $('#a2').val()); } else { $('#a3').val($('#a1').val() * $('#a2').val()); $('#a4').val($('#a3').val() * 0.5); }} </script> </head> <body> <div>Lengde <input id="a1" type="text" value="0" /></div> <div>Bredde <input id="a2" type="text" value="0" /></div> <div><input type="checkbox" name="checkbox" class="checkbox_check"></div> <div>Antall kvm <input id="a3" type="text" value="0" /></div> <div>Pris <input id="a4" type="text" value="0" /></div> </body> </html>