【问题标题】:How to execute different query based on returned value?如何根据返回值执行不同的查询?
【发布时间】:2019-03-19 17:48:06
【问题描述】:

我有一个足球比赛列表,定义如下:

id |        datetime      | status | gameweek | round_id | home_team_id
 1   2019-03-31 00:00:00      1          29     12696          1243
 2   2019-03-31 00:00:00      1          29     12696          1248
 3   2019-03-31 00:00:00      1          29     12696          1242
 4   2019-03-31 00:00:00      1          29     12696          1246
 5   2019-03-31 00:00:00      1          29     12696          1244
 6   2019-03-31 00:00:00      1          29     12696          1247
 7   2019-03-31 20:30:00      1          29     12696          1241
 8   2019-03-31 00:00:00      1          29     12696          1249
 9   2019-03-31 00:00:00      1          29     12696          2981
 10  2019-03-31 00:00:00      1          29     12696          1259

我需要将所有具有gameweek 和下一个gameweekmatches 归还给已完成的matches,而不是全部。

一些rounds 没有任何gameweek,所以在这种情况下应该返回所有matches,在完成的match 旁边有一个datetime

我写的查询是这样的:

Select m.* from `match` m where round_id = 12696 and m.datetime = (SELECT COALESCE(MIN(CASE WHEN m2.status < 5 THEN m2.datetime END), MAX(m2.datetime)) FROM `match` m2 WHERE m2.round_id = m.round_id)

这个只返回9条记录,我不明白为什么,唯一的原因是一条记录也有时间。

finishedmatches 是什么意思?

对于matches 结束或完成,我的意思是每个match 的状态是531 的状态表示match 已预定,但尚未播放; 5 表示 finished3 已取消。

例如:

id |        datetime      | status | gameweek | round_id | home_team_id
 1   2019-03-20 00:00:00      5          29     12696          1243
 2   2019-03-20 00:00:00      5          29     12696          1248
 3   2019-03-20 00:00:00      5          29     12696          1242
 4   2019-03-31 00:00:00      1          29     12696          1246
 5   2019-03-31 00:00:00      1          29     12696          1244
 6   2019-03-31 00:00:00      1          29     12696          1247
 7   2019-03-31 20:30:00      1          29     12696          1241
 8   2019-03-31 00:00:00      1          29     12696          1249
 9   2019-03-31 00:00:00      1          29     12696          2981
 10  2019-03-31 00:00:00      1          29     12696          1259

如您所见,前三张唱片已经播放完毕。在这种情况下,查询需要返回所有matches(已播放和预定),因为gameweek 29 还包含其他尚未播放的matches,因此预期结果是所有10 条记录。

预期结果:1、2、3、4、5、6、7、8、9、10

另外重要的是有些round没有gameweek,所以假设这样,我们需要返回即将到来的matches,例如:

id |        datetime      | status | gameweek | round_id | home_team_id
 1   2019-03-20 00:00:00      5        NULL     12696          1243
 2   2019-03-20 00:00:00      5        NULL     12696          1248
 3   2019-03-20 00:00:00      5        NULL     12696          1242
 4   2019-03-31 00:00:00      1        NULL     12696          1246
 5   2019-03-31 00:00:00      1        NULL     12696          1244
 6   2019-03-31 00:00:00      1        NULL     12696          1247
 7   2019-03-31 20:30:00      1        NULL     12696          1241
 8   2019-03-31 00:00:00      1        NULL     12696          1249
 9   2019-03-31 00:00:00      1        NULL     12696          2981
 10  2019-03-31 00:00:00      1        NULL     12696          1259

预期结果:4、5、6、7、8、9、10

(在小提琴中缺少记录 7)。

如果没有gameweeks,但所有matches都已完成(状态5),那么我们需要返回最新datetime的所有matches,例如:

id |        datetime      | status | gameweek | round_id | home_team_id
 1   2019-03-20 00:00:00      5        NULL     12696          1243
 2   2019-03-20 00:00:00      5        NULL     12696          1248
 3   2019-03-20 00:00:00      5        NULL     12696          1242
 4   2019-03-31 00:00:00      5        NULL     12696          1246
 5   2019-03-31 00:00:00      5        NULL     12696          1244
 6   2019-03-31 00:00:00      5        NULL     12696          1247
 7   2019-03-31 20:30:00      5        NULL     12696          1241
 8   2019-03-31 00:00:00      5        NULL     12696          1249
 9   2019-04-05 00:00:00      5        NULL     12696          2981
 10  2019-04-05 00:00:00      5        NULL     12696          1259

预期结果:9、10

id |        datetime      | status | gameweek | round_id | home_team_id
 1   2019-03-20 00:00:00      5        28     12696          1243
 2   2019-03-20 00:00:00      5        28     12696          1248
 3   2019-03-20 00:00:00      1        28     12696          1242
 4   2019-03-31 00:00:00      1        28     12696          1246
 5   2019-04-05 00:00:00      5        29     12696          1244
 6   2019-04-05 00:00:00      5        29     12696          1247
 7   2019-04-05 20:30:00      5        29     12696          1241
 8   2019-04-05 00:00:00      5        29     12696          1249

预期结果: 1,2,3,4,

我在这里创建了一个fiddle,它涵盖了所有情况。

【问题讨论】:

  • 你应该澄清你的问题:“在完成的比赛旁边”是什么意思?你能举一些例子吗?如果是过去,不是所有的比赛都结束了,如果在未来,不是所有的比赛都没有结束吗?您的查询示例不可能是正确的,因为没有定义 m2。目前还不是很清楚您要达到的目标。
  • @Dinu 嗨,感谢您对这个问题感兴趣。我用我错过的其他一些细节更新了这个问题,对此感到抱歉。如果您有任何其他问题,请告诉我,我也会澄清。
  • @sfarzoso:我相信,真正有帮助的是提供一组数据,这些数据通常代表您的用例,以及相应的结果期待。如果要求太复杂,可能需要几组样本数据/预期输出。
  • @sfarzoso ,我很想知道.. “这仅返回 8 条记录,我不明白为什么” .. 这 8 条记录是否返回正确数据?
  • 好吧,伙计们,我更新了问题,使其更清晰,我还添加了一个小提琴:dbfiddle.uk/…,涵盖了所有案例,如果您对该问题还有其他问题,请告诉我

标签: mysql sql


【解决方案1】:

这感觉像是一个不必要的复杂查询,但它确实与上述输出相匹配。可能是一个很好的起点。

with current_round as (
    select * 
    from match_case_1
    where round_id = 12696
)
select *
from current_round cr
where
    (
        not exists(select * from current_round where gameweek is null)
    )
    or 
    (
        exists(select * from current_round where status = 1) 
        and not exists(select * from current_round where gameweek is not null)
        and cr.status = 1    
    )
    or 
    (
        not exists(select * from current_round where status = 1)
        and not exists(select * from current_round where gameweek is not null)
        and cast(cr.`datetime` as date) = (
            select max(cast(`datetime` as date)) as `date`
            from current_round
            where status = 5 or status = 3
        )
    );

编辑

关于已发布场景的 DB Fiddle 查询

  1. https://www.db-fiddle.com/f/2f7NEPo72tUM7zLHBX2GXQ/0
  2. https://www.db-fiddle.com/f/3ghG6zf7hv2SbACL9vtnJa/1
  3. https://www.db-fiddle.com/f/q7DgtJRfDxyPA8bQheRncA/1
  4. https://www.db-fiddle.com/f/tV7VhZg1Ywfx1YmnFMtZdh/1

【讨论】:

  • 当然,但如果你这样做,你可能会牺牲一点清晰度。首先是摆脱 CTE,因为它是添加的,以便我可以在每种情况之间快速切换。然后您可以将其中的一些由于重复而组合起来,例如查找是否有任何游戏周值。像这样减少查询长度,您可能会不清楚每个部分的描述
  • 您的查询似乎涵盖了几乎所有情况,除了一个:db-fiddle.com/f/3ghG6zf7hv2SbACL9vtnJa/2 如果您查看 29 日之后还有另一个游戏周,在这种情况下需要返回记录游戏周 29 中并非所有其他记录,例如:1,2,3,4,5,6,7,8,9,10 您的查询返回所有..
【解决方案2】:

鉴于您的匹配项看起来不会经常更改,我的建议是省去您的麻烦,并在您的应用程序层中拆分这些查询(如果存在)

从您的示例中,您可以向您的数据库询问一系列问题以获得您想要的结果

第 12696 轮是否有未完成比赛的最低比赛周?

SELECT MIN(gameweek) min_gameweek 
  FROM `match` 
 WHERE round_id = 12696
   AND gameweek IS NOT NULL
   AND status = 1

如果是.. 那个比赛周的比赛是什么?

SELECT *
  FROM `match` 
 WHERE round_id = 12696
   AND gameweek = :min_gameweek

如果没有.. (您可能希望在这里找到第 12696 轮已完成比赛的最长比赛周数,并列出所有比赛)

如果还是没有。第 12696 轮没有比赛周的未完成比赛是什么?

SELECT *
  FROM `match`
 WHERE round_id = 12696
   AND gameweek IS NULL
   AND status = 1

如果没有.. 他们是第 12696 轮没有比赛周的已完成比赛的最新日期时间吗?

SELECT MAX(`datetime`) latest_datetime
  FROM `match`
 WHERE round_id = 12696
   AND gameweek IS NULL
   AND status != 1

如果是.. 第 12696 轮中没有比赛周且日期时间最近的已完成比赛有哪些?

SELECT *
  FROM `match` 
 WHERE round_id = 12696
   AND `datetime` = :latest_datetime

注意这种方法涉及更多查询,但可读性、可调试性和灵活性要好得多,甚至可能比尝试一个怪物查询更好

您可能可以将上面的一些查询与添加过多的不透明度结合起来,但我仍然会先写出每个步骤并对其进行测试

我也很认真地建议将整个过程简化如下

  • 存储圆桌中每轮的当前比赛周和最大比赛周(如果该回合没有比赛周,则为空)
  • 仅显示当前比赛周中相关回合的所有比赛
  • 当一场比赛结束时(或在一天/一周结束时),如果所有比赛都结束并且尚未达到最大值,则增加当前比赛周

【讨论】:

  • 我喜欢您组织查询的方式,但是您的逻辑假设在获得结果之前执行不同的查询,那么我应该如何实现该解决方案?具体来说,我应该检查每个查询是否返回一个结果,如果没有执行下一个?
  • 是的,我建议单独运行每个查询,在必要时检查结果,然后根据结果运行其他查询。这可以在您的项目的应用层中完成(如果您的项目有)
  • 相对于 1 次查询和多次检查,这种解决方案的性能不是更昂贵吗?
  • @sfarzoso 也许,但不一定。一个怪物查询将非常复杂,可能会导致引擎做额外的工作,检查和连接你可以通过在单独的部分中进行查询而打折的事情。随着表的增长或变化,单个复杂查询的执行计划也更有可能发生变化。这可能导致意外减速。即使分离查询最终会变慢,如果减速很明显,我也会感到非常惊讶
【解决方案3】:

这是你想要的吗?:

SELECT m.*
FROM `match` m 
WHERE m.round_id = 48 AND 
      m.gameweek = (SELECT MAX(m1.gameweek) 
                    FROM `match` m1 
                    WHERE m1.round_id = m.round_id AND m1.status = 1
                   );

通过这种方式,您将获得每场比赛的最后一场比赛gameweek

【讨论】:

    【解决方案4】:

    只需使用 1 周未来过滤器

     select m.* from `match` m 
     WHERE DATE(datetime)>=date(now())
     and DATE(datetime) <= date_add(current_date, INTERVAL 1 week)       
     and m.status =1
    

    【讨论】:

    • 如果您检查我的更新,您会发现您的查询并未涵盖所有情况,事实上最后一种情况不起作用,但其他情况是正确的,您能解决这个问题吗?
    • @sfarzoso 你怎么知道没有比赛周
    • '因为gameweek字段为NULL,有些比赛没有比赛周数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多