J'ai fait un SQL Fiddle pour cette question si cela facilite les choses pour n'importe qui.
J'ai en quelque sorte une base de données sur les sports fantastiques et ce que j'essaie de comprendre, c'est comment trouver des données de "séquence actuelle" (comme 'W2' si l'équipe a remporté ses 2 derniers affrontements, ou 'L1' si elle a perdu leur dernier match après avoir remporté le match précédent - ou «T1» s'ils ont égalé leur dernier match).
Voici mon schéma de base:
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
)
Une valeur de NULL
dans la winning_team_id
colonne indique une égalité pour cette correspondance.
Voici un exemple de déclaration DML avec des exemples de données pour 6 équipes et 3 semaines de matchs:
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
Voici un exemple de la sortie souhaitée (basée sur le DML ci-dessus) que j'ai du mal à même commencer à comprendre comment dériver:
| TEAM_ID | STEAK_TYPE | STREAK_COUNT |
|---------|------------|--------------|
| 1 | T | 1 |
| 2 | W | 3 |
| 3 | W | 3 |
| 4 | T | 1 |
| 5 | L | 2 |
| 6 | L | 1 |
J'ai essayé différentes méthodes en utilisant des sous-requêtes et des CTE mais je ne peux pas les assembler. Je voudrais éviter d'utiliser un curseur car je pourrais avoir un grand ensemble de données pour exécuter cela contre à l'avenir. J'ai l'impression qu'il pourrait y avoir un moyen impliquant des variables de table qui joignent ces données à lui-même, mais je travaille toujours dessus.
Informations supplémentaires: Il pourrait y avoir un nombre variable d'équipes (n'importe quel nombre pair entre 6 et 10) et le total des affrontements augmentera de 1 pour chaque équipe chaque semaine. Avez-vous des idées sur la façon de procéder?
la source
bigint
pour autant de colonnes oùint
ferait probablement 3) pourquoi tous les_
s?! 4) Je préfère que les noms de table soient singuliers, mais je reconnais que tout le monde n'est pas d'accord avec moi // mais ceux à part ce que vous nous avez montré ici semblent cohérents, ouiRéponses:
Puisque vous êtes sur SQL Server 2012, vous pouvez utiliser quelques-unes des nouvelles fonctions de fenêtrage.
SQL Fiddle
C1
calcule lestreak_type
pour chaque équipe et match.C2
trouve le précédentstreak_type
ordonné parmatch_id desc
.C3
génère une somme cumuléestreak_sum
ordonnée parmatch_id desc
gardant a0
aussi longtemps que lastreak_type
est la même que la dernière valeur.La requête principale résume les séquences où se
streak_sum
trouve0
.la source
LEAD()
. Pas assez de gens connaissent les nouvelles fonctions de fenêtrage en 2012FantasyTeams JOIN FantasyMatches
avecFantasyMatches CROSS APPLY (VALUES (home_fantasy_team_id), (away_fantasy_team_id))
et donc potentiellement améliorer les performances.FantasyTeams
il est probablement préférable de rejoindre la requête principale à la place.Une approche intuitive pour résoudre ce problème est la suivante:
Cette stratégie pourrait l'emporter sur la solution de fonction de fenêtre (qui effectue une analyse complète des données) à mesure que la table s'agrandit, en supposant que la stratégie récursive est mise en œuvre efficacement. La clé du succès est de fournir des index efficaces pour localiser rapidement les lignes (à l'aide de recherches) et éviter les tris. Les index nécessaires sont:
Pour aider à l'optimisation des requêtes, j'utiliserai une table temporaire pour contenir les lignes identifiées comme faisant partie d'une séquence en cours. Si les séquences sont généralement courtes (comme c'est malheureusement le cas pour les équipes que je suis), ce tableau devrait être assez petit:
Ma solution de requête récursive est la suivante ( SQL Fiddle ici ):
Le texte T-SQL est assez long, mais chaque section de la requête correspond étroitement au schéma général du processus donné au début de cette réponse. La requête est allongée par la nécessité d’utiliser certaines astuces pour éviter les tris et produire un
TOP
dans la partie récursive de la requête (ce qui n'est normalement pas autorisé).Le plan d'exécution est relativement petit et simple par rapport à la requête. J'ai ombré la région d'ancrage en jaune et la partie récursive en vert dans la capture d'écran ci-dessous:
Avec les lignes de séquence capturées dans un tableau temporaire, il est facile d'obtenir les résultats récapitulatifs dont vous avez besoin. (L'utilisation d'une table temporaire évite également un déversement de tri qui pourrait se produire si la requête ci-dessous était combinée avec la requête récursive principale)
La même requête peut être utilisée comme base pour la mise à jour de la
FantasyTeams
table:Ou, si vous préférez
MERGE
:L'une ou l'autre approche produit un plan d'exécution efficace (basé sur le nombre connu de lignes dans la table temporaire):
Enfin, parce que la méthode récursive inclut naturellement le
match_id
dans son traitement, il est facile d'ajouter une liste desmatch_id
s qui forment chaque séquence à la sortie:Production:
Plan d'exécution:
la source
EXISTS (... INTERSECT ...)
au lieu de justeStreaks.streak_type = CASE ...
? Je sais que l'ancienne méthode peut être utile lorsque vous devez faire correspondre des valeurs NULL des deux côtés ainsi que des valeurs, mais ce n'est pas comme si la bonne partie pouvait produire des valeurs NULL dans ce cas, donc ...CASE
est utilisé, l'optimiseur ne peut pas utiliser une concaténation de fusion (qui préserve l'ordre des clés d'union) et utilise une concaténation plus des tris à la place.Une autre façon d'obtenir le résultat est par un CTE récursif
Démo SQLFiddle
la source