Избегание повторных подзапросов, когда «WITH» недоступно - PullRequest
2 голосов
/ 16 марта 2010

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)
)

Я пропустил что-то действительно очевидное или мне просто не следует пытаться сделать это одним запросом? Или текущая сложность отражает плохой дизайн стола?

1 Ответ

1 голос
/ 16 марта 2010

Попробуйте выбрать нужные данные (имена команд и 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;

Формат результата для более чем двух команд на одно устройство не был указан, поэтому возможна дальнейшая настройка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...