Я сделал SQL-скрипку для этого вопроса, если это облегчает жизнь кому-либо.
У меня есть своего рода база данных по фэнтези-спорту, и я пытаюсь выяснить, как получить данные о «текущей серии» (например, «W2», если команда выиграла последние 2 матча, или «L1», если они проиграли). их последний поединок после победы в предыдущем поединке - или «T1», если они связали свой последний поединок).
Вот моя основная схема:
CREATE TABLE FantasyTeams (
team_id BIGINT NOT NULL
)
CREATE TABLE FantasyMatches(
match_id BIGINT NOT NULL,
home_fantasy_team_id BIGINT NOT NULL,
away_fantasy_team_id BIGINT NOT NULL,
fantasy_season_id BIGINT NOT NULL,
fantasy_league_id BIGINT NOT NULL,
fantasy_week_id BIGINT NOT NULL,
winning_team_id BIGINT NULL
)
Значение NULL
вwinning_team_id
столбце указывает на связь для этого совпадения.
Вот пример заявления DML с некоторыми примерами данных для 6 команд и матчей за 3 недели:
INSERT INTO FantasyTeams
SELECT 1
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 6
INSERT INTO FantasyMatches
SELECT 1, 2, 1, 2, 4, 44, 2
UNION
SELECT 2, 5, 4, 2, 4, 44, 5
UNION
SELECT 3, 6, 3, 2, 4, 44, 3
UNION
SELECT 4, 2, 4, 2, 4, 45, 2
UNION
SELECT 5, 3, 1, 2, 4, 45, 3
UNION
SELECT 6, 6, 5, 2, 4, 45, 6
UNION
SELECT 7, 2, 6, 2, 4, 46, 2
UNION
SELECT 8, 3, 5, 2, 4, 46, 3
UNION
SELECT 9, 4, 1, 2, 4, 46, NULL
GO
Вот пример желаемого вывода (основанного на DML выше), который у меня возникли проблемы, даже начинающий выяснять, как получить:
| TEAM_ID | STEAK_TYPE | STREAK_COUNT |
|---------|------------|--------------|
| 1 | T | 1 |
| 2 | W | 3 |
| 3 | W | 3 |
| 4 | T | 1 |
| 5 | L | 2 |
| 6 | L | 1 |
Я пробовал различные методы, используя подзапросы и CTE, но я не могу собрать их вместе. Я хотел бы избежать использования курсора, так как у меня мог бы быть большой набор данных для выполнения этого в будущем. Я чувствую, что может быть способ с использованием табличных переменных, которые каким-то образом соединяют эти данные с самим собой, но я все еще работаю над этим.
Дополнительная информация: может быть различное количество команд (любое четное число от 6 до 10), и общее количество матчей будет увеличиваться на 1 для каждой команды каждую неделю. Есть идеи, как мне это сделать?
источник
bigint
для такого количества столбцов, гдеint
, вероятно, будет 3), почему все_
s ?! 4) Я предпочитаю, чтобы имена таблиц были единичными, но признаю, что не все согласны со мной // но те, что вы здесь показали, выглядят согласованно, даОтветы:
Поскольку вы работаете на SQL Server 2012, вы можете использовать несколько новых оконных функций.
SQL Fiddle
C1
рассчитываетstreak_type
для каждой команды и матча.C2
находит предыдущийstreak_type
заказанныйmatch_id desc
.C3
генерирует промежуточную сумму,streak_sum
упорядоченную,match_id desc
сохраняя a0
long, посколькуstreak_type
значение совпадает с последним значением.Основной запрос суммирует полосы, где
streak_sum
есть0
.источник
LEAD()
. Недостаточно людей знают о новых оконных функциях в 2012 годуFantasyTeams JOIN FantasyMatches
сFantasyMatches CROSS APPLY (VALUES (home_fantasy_team_id), (away_fantasy_team_id))
и , таким образом , потенциально повысить производительность.FantasyTeams
лучше присоединиться к основному запросу.Один интуитивный подход к решению этой проблемы:
Эта стратегия может превзойти решение для оконных функций (которое выполняет полное сканирование данных) по мере увеличения таблицы при условии, что рекурсивная стратегия реализована эффективно. Ключом к успеху является предоставление эффективных индексов для быстрого поиска строк (с помощью поиска) и предотвращения сортировки. Необходимые индексы:
Чтобы помочь в оптимизации запросов, я буду использовать временную таблицу для хранения строк, идентифицированных как формирующие часть текущей полосы. Если полосы, как правило, короткие (как это верно для команд, за которыми я следую, к сожалению), эта таблица должна быть довольно маленькой:
Мое рекурсивное решение запросов выглядит следующим образом ( SQL Fiddle здесь ):
Текст T-SQL довольно длинный, но каждый раздел запроса близко соответствует общей схеме процесса, приведенной в начале этого ответа. Запрос выполняется дольше из-за необходимости использовать определенные приемы, чтобы избежать сортировок и создать
TOP
рекурсивную часть запроса (что обычно не разрешается).План выполнения является относительно небольшим и простым по сравнению с запросом. Я закрасил область привязки желтым цветом, а рекурсивную часть - зеленым на снимке экрана ниже:
С помощью строк строк, захваченных во временную таблицу, легко получить требуемые итоговые результаты. (Использование временной таблицы также позволяет избежать различий в сортировке, которые могут возникнуть, если запрос ниже объединен с основным рекурсивным запросом)
Этот же запрос можно использовать как основу для обновления
FantasyTeams
таблицы:Или, если вы предпочитаете
MERGE
:Любой подход дает эффективный план выполнения (основанный на известном количестве строк во временной таблице):
Наконец, поскольку рекурсивный метод естественным образом включает
match_id
в свою обработку метод , легко добавить списокmatch_id
s, которые формируют каждую последовательность, в вывод:Выход:
План выполнения:
источник
EXISTS (... INTERSECT ...)
вместо простоStreaks.streak_type = CASE ...
? Я знаю, что первый метод может быть полезен, когда вам нужно сопоставить значения NULL на обеих сторонах, а также значения, но это не так, как если бы правая часть могла генерировать любые значения NULL в этом случае, так что ...CASE
используется, оптимизатор не может использовать конкатенацию слиянием (которая сохраняет порядок ключей объединения) и вместо этого использует конкатенацию плюс сортировки.Другой способ получить результат - с помощью рекурсивного CTE.
SQLFiddle demo
источник