SQL-арифметика и объединение трех столбцов - PullRequest
0 голосов
/ 25 марта 2011

У меня есть схема, которая выглядит следующим образом:

+----------+
| tour     |
+----------+
| id       |
| name     |
+----------+

+----------+
| golfer   |
+----------+
| id       |
| name     |
| tour_id  |
+----------+

+-----------+
| stat      |
+-----------+
| id        |
| round     |
| score     |
| golfer_id |
+-----------+

Так что, по сути, тур по гольфу включает в себя X игроков в гольф. У игрока в гольф будет X количество характеристик. Круглый столбец в таблице статистики содержит только цифры (1, 2, 3, 4 ... и т. Д.). Они не обязательно одно за другим, но они уникальны.

Теперь я хочу найти всех игроков в гольф, участвующих в туре "PGA", и для каждого из этих игроков подвести итоги за последние 2 раунда. Последние 2 раунда - это, по сути, строки в таблице статистики для игрока с самыми большими двумя числами. Допустим, гольфист «Тайгер Вудс» играл в 1, 3, 6 и 10 раундах, тогда я хочу лишь подсчитать его результаты в 6 и 10 раундах. Другое требование - я не хочу показывать игроков в гольф, которые еще играть как минимум в двух раундах.

Я пробовал несколько способов добиться этого, но всегда запутывался.

Ответы [ 2 ]

2 голосов
/ 25 марта 2011

HSQLDB 2.1 поддерживает LATERAL-объединения, которые позволяют выбирать этот тип по произвольным критериям.

Простое объединение перечислит всех игроков в гольф в туре PGA:

select golfer.name from tour join golfer on (tour.id = tour_id and  tour.name = 'PGA')

Затем вы ЛАТЕРАЛЬНО присоединяетесь к этой таблице столько раз, сколько вам нужно для конкретного счета. Следующий пример включает в себя счет за последний раунд (только если игра сыграла раунд)

select golfer.name, firststat.score from tour join golfer on (tour.id = tour_id and  tour.name = 'PGA' ),
 lateral(select * from stat where golfer_id = golfer.id order by round desc limit 1) firststat

В следующем примере вы используете еще одно боковое соединение, чтобы включить последний, но один раунд. Если игрок не провёл два раунда, для него не будет строки:

select golfer.name, secondstat.score score1, firststat.score score2 from tour join golfer on (tour.id = tour_id and  tour.name = 'PGA' ), 
 lateral(select * from stat where golfer_id = golfer.id order by round desc limit 1 offset 1) secondstat, 
 lateral(select * from stat where golfer_id = golfer.id order by round desc limit 1) firststat

LATERAL соединение не нуждается в предложении WHERE, потому что «условие условия» берется из таблиц в списке FROM, которые появляются перед текущей таблицей. Поэтому операторы SELECT в подзапросах таблиц LATERAL могут использовать golfer.id из первой объединенной таблицы.

2 голосов
/ 25 марта 2011

Если вы просто хотите последние два раунда (подчеркните «два»), есть простой трюк. Этот трюк не распространяется на получение более двух или не последних двух записей. Для получения произвольных записей в разделе вам придется использовать оконные функции, которые более активны и поддерживаются только в более новых версиях основных модулей баз данных.

Хитрость заключается в том, чтобы самостоятельно соединить таблицу «stat» с собой по идентификатору игрока в гольф. Таким образом, вы получаете все комбинации любых двух раундов игрока в гольф, включая комбинации с одинаковым раундом:

SELECT s1.round as s1_round, s2.round AS s2_round
FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id)

Затем вы исключаете (через предложение WHERE) комбинации, которые имеют одинаковые раунды, а также убедитесь, что эти комбинации всегда первый раунд> второй раунд. Это означает, что теперь у вас есть все комбинации любых двух раундов игрока в гольф, без дубликатов:

SELECT s1.round as s1_round, s2.round AS s2_round
FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id)
WHERE s1.round > s2.round

Обратите внимание, что если вы выбираете только записи для конкретного игрока в гольф и сортируете DESC по двум круглым столбцам, в верхнем ряду будут последние два раунда этого игрока в гольф:

SELECT TOP 1 s1.round as s1_round, s2.round AS s2_round
FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id)
WHERE s1.round > s2.round
ORDER BY s1.round DESC, s2.round DESC

TOP 1 является языком SQL Server для получения верхней строки. Для MySQL вам нужно использовать LIMIT 1. Для других баз данных используйте особый способ ядра базы данных.

Однако, в этом случае вы не можете сделать это так просто, потому что вам нужны последние два раунда ВСЕХ игроков в гольф. Вы должны будете сделать больше объединений:

SELECT id,
   (SELECT MAX(s1.round) FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id)
    WHERE s1.round > s2.round AND s1.golfer_id = golfer.id) AS last_round,
   (SELECT MAX(s2.round) FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id)
    WHERE s1.round > s2.round AND s1.golfer_id = golfer.id) AS second_to_last_round
FROM golfer

Это даст вам два последних раунда (в двух столбцах) для каждого игрока в гольф.

Или соединение таблицы игроков в гольф с установленным временным набором из двух столбцов также должно работать:

SELECT golfer.id, MAX(r.s1_round) AS last_round, MAX(r.s2_round) AS second_to_last_round
FROM golfer INNER JOIN 
(
 SELECT s1.golfer_id AS golfer_id, s1.round AS s1_round, s2.round AS s2_round
 FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id)
 WHERE s1.round > s2.round
) r ON (r.golfer_id = golfer.id)
GROUP BY golfer.id

Я оставляю за собой тривиальное упражнение: присоединить этот запрос к таблице турниров, чтобы получить игроков в гольф для тура PGA, и присоединить этот запрос к таблице статистики, чтобы получить результаты двух последних раундов.

...