【问题标题】:Avoiding repeated subqueries when 'WITH' is unavailable当“WITH”不可用时避免重复子查询
【发布时间】:2010-03-16 00:59:13
【问题描述】:

MySQL v5.0.58.

为简洁起见,省略了带有外键约束等的表和其他不相关的细节:

CREATE TABLE `fixture` (
  `id` int(11) NOT NULL auto_increment,
  `competition_id` int(11) NOT NULL,
  `name` varchar(50) NOT NULL,
  `scheduled` datetime default NULL,
  `played` datetime default NULL,
  PRIMARY KEY  (`id`)
);

CREATE TABLE `result` (
  `id` int(11) NOT NULL auto_increment,
  `fixture_id` int(11) NOT NULL,
  `team_id` int(11) NOT NULL,
  `score` int(11) NOT NULL,
  `place` int(11) NOT NULL,
  PRIMARY KEY  (`id`)
);

CREATE TABLE `team` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
);

地点:

  • 平局会将result.place 设置为0
  • result.place 将包含一个整数,表示第一名、第二名等

任务是返回一个字符串,描述给定团队在给定比赛中最近的比赛结果。如果给定的球队获胜,格式应该是“def Team X,Team Y”,如果给定的球队输了,格式应该是“输给 Team X”,如果是平局,格式应该是“与 Team X 打成平手”。是的,理论上每场比赛可能有超过两支球队(尽管 1 对 1 将是最常见的情况)。

这可行,但感觉效率很低:

SELECT CONCAT(
    (SELECT CASE `result`.`place` 
        WHEN 0 THEN "drew with"
        WHEN 1 THEN "def"
        ELSE "lost to"
        END
    FROM `result`
    WHERE `result`.`fixture_id` = 
        (SELECT `fixture`.`id` FROM `fixture`
        LEFT JOIN `result` ON `result`.`fixture_id` = `fixture`.`id`
        WHERE `fixture`.`competition_id` = 2
        AND `result`.`team_id` = 1
        ORDER BY `fixture`.`played` DESC
        LIMIT 1)
    AND `result`.`team_id` = 1),
    ' ',
    (SELECT GROUP_CONCAT(`team`.`name`)
    FROM `fixture`
    LEFT JOIN `result` ON `result`.`fixture_id` = `fixture`.`id`
    LEFT JOIN `team` ON `result`.`team_id` = `team`.`id`
    WHERE `fixture`.`id` = 
        (SELECT `fixture`.`id` FROM `fixture`
        LEFT JOIN `result` ON `result`.`fixture_id` = `fixture`.`id`
        WHERE `fixture`.`competition_id` = 2
        AND `result`.`team_id` = 1
        ORDER BY `fixture`.`played` DESC
        LIMIT 1)
    AND `team`.`id` != 1)
)

我是否遗漏了一些非常明显的事情,或者我不应该尝试在一个查询中执行此操作?还是当前的困难反映了糟糕的桌子设计?

【问题讨论】:

  • 我刚刚意识到“输给”的情况需要更加小心处理,因为它真的应该只列出获胜的团队。
  • 有多少支球队可以参加一场比赛?
  • 我没有为此设置边界,因此我当前的表格设计中固定装置与结果是分开的。有些游戏(我们这里指的是电脑游戏)每场比赛有三方或更多方,例如《战争黎明》等 RTS。
  • 当每场比赛有超过两支球队时,输出格式应该是什么?特别是,是否应该列出击败失败球队的每支球队(例如,目标是 Z 队:“输给 X 队,Y 队”)?是否应该将每支被击败的球队都列为获胜球队(例如,目标是 X 队:“def Team Y, Team Z; Team X”)?如果是这样,获胜的团队应该只是列出的最后一个团队,还是应该使用不同的分隔符(例如“;”而不是“,”)将其与其他团队分开?
  • 是的,我认为如果球队输了,只有获胜者(place=1)应该被列为“输给X队”。如果球队获胜,则应列出所有失败的球队。

标签: sql mysql subquery


【解决方案1】:

尝试选择您需要的数据(团队名称和目标团队的place),然后将其组合起来。每场比赛两支球队:

SELECT CASE `recent`.`place` 
        WHEN 0 THEN CONCAT("drew with ", other_name)
        WHEN 1 THEN CONCAT("def ", other_name, ", ", targ_name)
        ELSE CONCAT("lost to ", other_name)
    END
FROM (SELECT rtarg.place, targ.name AS targ_name, other.name AS other_name
        FROM `fixture`
        JOIN `result` AS rtarg ON `rtarg`.`fixture_id` = `fixture`.`id`
        JOIN `team` AS targ ON `rtarg`.`team_id` = `targ`.`id`
        JOIN `result` AS rother ON `rother`.`fixture_id` = `fixture`.`id`
        JOIN `team` AS other ON `rother`.`team_id` = `other`.`id`
        WHERE `fixture`.`competition_id` = 2
          AND `rtarg`.`team_id` = @targ
          AND `rother`.`team_id` != @targ
        ORDER BY `fixture`.`played` DESC
        LIMIT 1) AS `recent`;

只需极少改动即可处理每个夹具两个以上的团队,但其他子查询也可以。

SELECT CASE `recent`.`place` 
        WHEN 0 THEN CONCAT("drew with ", other_names)
        WHEN 1 THEN CONCAT("def ", other_names, "; ", targ_name)
        ELSE CONCAT("lost to ", other_names)
    END
FROM (SELECT rtarg.place, targ.name AS targ_name, GROUP_CONCAT(other.name SEPARATOR ', ') AS other_names
        FROM `fixture`
        JOIN `result` AS rtarg ON `rtarg`.`fixture_id` = `fixture`.`id`
        JOIN `team` AS targ ON `rtarg`.`team_id` = `targ`.`id`
        JOIN `result` AS rother ON `rother`.`fixture_id` = `fixture`.`id`
        JOIN `team` AS other ON `rother`.`team_id` = `other`.`id`
        WHERE `fixture`.`competition_id` = 2
          AND `targ`.`id` = @targ
          AND `rother`.`team_id` != @targ
          AND ((rtarg.place<=1 AND rother.place >= rtarg.place)
            OR (rtarg.place>1 AND rother.place < rtarg.place))
        GROUP BY fixture.id
        ORDER BY `fixture`.`played` DESC
        LIMIT 1
) AS recent;

没有指定每场比赛超过两支球队的结果格式,因此可能需要进一步调整。

【讨论】:

  • 我喜欢这种方法,但不幸的是,它不允许每个夹具有超过两个团队的情况。除非我误解了?
  • 你明白了。我将很快更新我的答案,另外查询每个夹具两个以上的球队。
  • 非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2016-06-28
  • 2021-02-15
  • 1970-01-01
  • 1970-01-01
  • 2011-07-25
  • 2016-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多