【问题标题】:mysql complex Joinmysql复杂加入
【发布时间】:2012-04-22 03:14:11
【问题描述】:

我有一个由 7 个表组合而成的 select 语句

SELECT * (//instead of * i put in my code the proper columns i need from the selected tabls)
FROM 
ev_events_eve
LEFT JOIN ev_events_multilang_evmulti ON ev_events_multilang_evmulti.event_evmulti = ev_events_eve.id_eve
LEFT JOIN ev_schedule_sch ON ev_schedule_sch.event_id_sch = ev_events_eve.id_eve
LEFT JOIN ev_events_type_et ON ev_schedule_sch.event_type_id_sch = ev_events_type_et.id_et
LEFT JOIN ev_events_type_multilang_etmulti ON ev_events_type_multilang_etmulti.idevent_type_etmulti = ev_events_type_et.id_et
LEFT JOIN ev_schedule_multilang_schmulti ON ev_schedule_multilang_schmulti.id_schedule_schmulti = ev_schedule_sch.id_sch
LEFT JOIN ev_timetable_tt ON ev_timetable_tt.schedule_id_tt = ev_schedule_sch.id_sch AND ev_timetable_tt.event_id_tt = ev_events_eve.id_eve
WHERE (ev_events_eve.active_eve = 1 AND ev_schedule_multilang_schmulti.iso_code_lang_schmulti = '{$current_language}' AND ev_schedule_sch.active_sch = 1) AND
ev_timetable_tt.end_date_tt > CURDATE() OR (ev_timetable_tt.weekday_tt = DAYNAME(CURDATE()) AND ev_timetable_tt.end_date_tt > CURDATE()) OR ev_timetable_tt.specific_date_tt > CURDATE()
ORDER BY ev_timetable_tt.start_date_tt ASC LIMIT 3

这个语句忽略where子句的问题,每次我使用mysql时,我都会创建示例sql语句,由两个或三个具有正常左连接或内连接的表组成,我需要有人与我讨论这个问题,它可以让我进一步了解我的代码中没有提到的东西,但是我知道上面的代码不足以让任何人与我一起处理这个问题,但我需要有人可以与我讨论

我用以下信息更新了问题

主要业务规则:

  • 该项目适用于多语言网站

  • 这些表格是:
    • ev_events_eve(事件实体,如 id、徽标、标语、开始和结束 整个活动的日期)
      • ev_events_multilang_evmulti(主赛事多语言表 表格,例如名称(en、fr、gr .. 等)、描述...等)
      • ev_schedule_sch(调度实体,如 id、事件表的外键)
      • ev_schedule_multilang_schmulti(多语言表为主 时间表)
      • ev_timetable_tt(存储与相关的开始和结束日期和时间 ev_schedule_sch 表)

  • 每个表都被复制到两个表中,其中一个用于存储 实体和另一个用于存储多语言数据

  • 表之间的关系
    • 主表是 ev_events_eve
    • ev_events_multilang_evmulti 通过外部关联到主表 键
    • ev_schedule_sch related 通过外键到主表
    • ev_schedule_multilang_schmultiev_schedule_sch 表相关,通过 外键
    • ev_timetable_tt 通过外键与ev_schedule_sch 相关

我需要编写一个语句来从事件表中检索传入的 3 行(假设我需要检索最新的 3 行,因为您没有所有字段的完整架构,稍后我们可以在WHERE 子句 :) ) 以及这 3 行还从与事件表中的每一行相关的计划表中检索 6 行 所以估计的结果一定是这样的 假设我有 4 个事件

  • 第一个被调用:事件#1
    • 此事件在日程相关表中有 3 行(所以检索
      所有这些行)


  • 第二次被调用:事件#2
    • 这在计划相关表中有 11 行(仅检索 6 他们的行)

  • 第 3 个被调用:event#3
    • 此事件在日程表中没有任何与其相关的行 相关表

  • 第 4 次调用:事件#4
    • 此事件在相关日程表中只有 1 行与之相关 表格

如上所述,我只需要检索日程表中的前 3 个事件和相关事件

【问题讨论】:

  • 这七个总是存在还是需要外连接?
  • 就像您指出的那样,很难回答您的问题,因为它的表述非常糟糕。请提供该语句如何“忽略 where 子句”的示例。我可以根据经验说这种情况不太可能发生,查询本身可能是不正确的。您期望退回什么,您会得到什么?
  • 您可能需要检查所有由 AND 和 OR 分隔的段是否正确包裹在括号中,如果不这样做可能会出现意外的逻辑解释
  • @Johnnyoh 我刚刚编辑了我的问题,并附上了我需要做的完整示例
  • 一个使查询“更小”且更具可读性的示例提示:LEFT JOIN ev_events_multilang_evmulti AS evm ON ...

标签: mysql


【解决方案1】:

我只是猜测问题出在查询的OR 部分。我只是在查询中更改了这部分:

AND ( 
ev_timetable_tt.end_date_tt > CURDATE() 
OR (ev_timetable_tt.weekday_tt = DAYNAME(CURDATE()) AND ev_timetable_tt.end_date_tt > CURDATE()) 
OR ev_timetable_tt.specific_date_tt > CURDATE()
)

可能你想这样做:(我给了别名以使其更具可读性)

SELECT * 
FROM ev_events_eve e
LEFT JOIN ev_events_multilang_evmulti eml ON eml.event_evmulti = e.id_eve
LEFT JOIN ev_schedule_sch es ON es.event_id_sch = e.id_eve
LEFT JOIN ev_events_type_et et ON es.event_type_id_sch = et.id_et
LEFT JOIN ev_events_type_multilang_etmulti emel ON emel.idevent_type_etmulti = et.id_et
LEFT JOIN ev_schedule_multilang_schmulti ems ON ems.id_schedule_schmulti = es.id_sch
LEFT JOIN ev_timetable_tt ett ON ett.schedule_id_tt = es.id_sch AND ett.event_id_tt = e.id_eve
WHERE (e.active_eve = 1 AND ems.iso_code_lang_schmulti = '{$current_language}' AND es.active_sch = 1) 
AND 
(
    ett.end_date_tt > CURDATE() 
OR (ett.weekday_tt = DAYNAME(CURDATE()) AND ett.end_date_tt > CURDATE()) 
OR ett.specific_date_tt > CURDATE()
)
ORDER BY ett.start_date_tt ASC
LIMIT 3

如果你喜欢你的语法(没有表别名):

SELECT * (//instead of * i put in my code the proper columns i need from the selected tabls)
FROM 
ev_events_eve
LEFT JOIN ev_events_multilang_evmulti ON ev_events_multilang_evmulti.event_evmulti = ev_events_eve.id_eve
LEFT JOIN ev_schedule_sch ON ev_schedule_sch.event_id_sch = ev_events_eve.id_eve
LEFT JOIN ev_events_type_et ON ev_schedule_sch.event_type_id_sch = ev_events_type_et.id_et
LEFT JOIN ev_events_type_multilang_etmulti ON ev_events_type_multilang_etmulti.idevent_type_etmulti = ev_events_type_et.id_et
LEFT JOIN ev_schedule_multilang_schmulti ON ev_schedule_multilang_schmulti.id_schedule_schmulti = ev_schedule_sch.id_sch
LEFT JOIN ev_timetable_tt ON ev_timetable_tt.schedule_id_tt = ev_schedule_sch.id_sch AND ev_timetable_tt.event_id_tt = ev_events_eve.id_eve
WHERE (ev_events_eve.active_eve = 1 AND ev_schedule_multilang_schmulti.iso_code_lang_schmulti = '{$current_language}' AND ev_schedule_sch.active_sch = 1) 
AND ( 
ev_timetable_tt.end_date_tt > CURDATE() 
OR (ev_timetable_tt.weekday_tt = DAYNAME(CURDATE()) AND ev_timetable_tt.end_date_tt > CURDATE()) 
OR ev_timetable_tt.specific_date_tt > CURDATE()
)
ORDER BY ev_timetable_tt.start_date_tt ASC LIMIT 3

【讨论】:

    【解决方案2】:

    LEFT JOINs 与WHERE 条件结合使用,这些条件在LEFT JOINed 的那些表中需要非NULL 值,这违背了使用LEFT JOIN 的目的。您要么必须在 WHERE 条件中指定值可以是 [whatever] OR NULL,要么使 WHERE 条件成为 ONJOIN 的一部分。

    例如:

    ... WHERE ... (ev_schedule_sch.active_sch = 1 OR ev_schedule_sch.active_sch IS NULL) ...
    

    或者

    ... LEFT JOIN ev_schedule_sch ON ev_schedule_sch.event_id_sch = ev_events_eve.id_eve AND ev_schedule_sch.active_sch = 1 ...
    

    【讨论】:

      【解决方案3】:

      首先,我也相信你在 where 子句中的日期标准应该被包裹起来以封装它。如果不是,则应用第一部分,然后在第一个“OR”合格后立即完全忽略它。下一个。您有许多 LEFT JOIN,这意味着只要左侧的记录是合格的,右侧表中的记录只有 OPTIONAL。但是,一旦您将条件添加到 WHERE 子句中针对所述右侧表,它就会抛出“LEFT”资格。您应该将该条件移动到 LEFT JOIN 条件而不是 where 子句中(正如我已调整的那样)。

      您的原始查询略微清理,使用别名并更正日期条件的括号,因此您不会得到错误的结果,但显示为通过 WHERE 子句将其转换为 NORMAL INNER 连接。

      SELECT *
         FROM 
            ev_events_eve EE
      
               LEFT JOIN ev_events_multilang_evmulti ELANG 
                  ON EE.id_eve = ELANG.event_evmulti
      
               LEFT JOIN ev_schedule_sch SCHED 
                  ON EE.id_eve = SCHED.event_id_sch
      
                  LEFT JOIN ev_events_type_et ETYPE
                     ON SCHED.event_type_id_sch = ETYPE.id_et
      
                     LEFT JOIN ev_events_type_multilang_etmulti ETMLANG 
                        ON ETYPE.id_et = ETMLANG.idevent_type_etmulti
      
                  LEFT JOIN ev_schedule_multilang_schmulti SMS 
                     ON SCHED.id_sch = SMS.id_schedule_schmulti
      
                  LEFT JOIN ev_timetable_tt TIMET 
                     ON SCHED.id_sch  = TIMET.schedule_id_tt
                    AND EE.id_eve = TIMET.event_id_tt
      
         WHERE 
                (   EE.active_eve = 1 
                AND SMS.iso_code_lang_schmulti = '{$current_language}' 
                AND SCHED.active_sch = 1 ) 
            AND (      TIMET.end_date_tt > CURDATE() 
                  OR (    TIMET.weekday_tt = DAYNAME(CURDATE()) 
                      AND TIMET.end_date_tt > CURDATE()) 
                  OR TIMET.specific_date_tt > CURDATE() )
         ORDER BY 
            TIMET.start_date_tt ASC LIMIT 3
      

      这里的查询被调整以保持各自的左连接,注意你的条件放置在哪里的差异。

      SELECT *
         FROM 
            ev_events_eve EE
      
               LEFT JOIN ev_events_multilang_evmulti ELANG 
                  ON EE.id_eve = ELANG.event_evmulti
      
               LEFT JOIN ev_schedule_sch SCHED 
                  ON EE.id_eve = SCHED.event_id_sch
                 AND SCHED.active_sch = 1
      
                  LEFT JOIN ev_events_type_et ETYPE
                     ON SCHED.event_type_id_sch = ETYPE.id_et
      
                     LEFT JOIN ev_events_type_multilang_etmulti ETMLANG 
                        ON ETYPE.id_et = ETMLANG.idevent_type_etmulti
      
                  LEFT JOIN ev_schedule_multilang_schmulti SMS 
                     ON SCHED.id_sch = SMS.id_schedule_schmulti
                    AND SMS.iso_code_lang_schmulti = '{$current_language}' 
      
                  LEFT JOIN ev_timetable_tt TIMET 
                     ON SCHED.id_sch  = TIMET.schedule_id_tt
                    AND EE.id_eve = TIMET.event_id_tt
                    AND (      TIMET.end_date_tt > CURDATE() 
                          OR (    TIMET.weekday_tt = DAYNAME( CURDATE() ) 
                              AND TIMET.end_date_tt > CURDATE() ) 
                          OR TIMET.specific_date_tt > CURDATE()    )
         WHERE 
            EE.active_eve = 1 
         ORDER BY 
            TIMET.start_date_tt ASC LIMIT 3
      

      最后一点。您的 ORDER BY 引用了“ev_TimeTable_tt”(别名为 TIMET)表,这对我来说意味着您总是希望在此表中只找到明确的匹配项。如果是这样,这实际上应该是 INNER JOIN 而不是 LEFT JOIN。

      由于进入“EV_TIMETABLE_TT”表需要从调度表中查找,我希望这也是 INNER JOIN 要求。

      因此,通过 LEFT 与 INNER join 的说明,您可能需要调整以适应这些组合。不知道你的数据,我真的无法进一步指导。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-10
        • 2014-02-10
        • 1970-01-01
        • 1970-01-01
        • 2023-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多