J'ai une table dont je veux obtenir la dernière entrée pour chaque groupe. Voici le tableau:
DocumentStatusLogs
Table
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
Le tableau sera regroupé par DocumentID
et trié par DateCreated
ordre décroissant. Pour chacun DocumentID
, je veux obtenir le dernier statut.
Ma sortie préférée:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
Existe-t-il une fonction d'agrégation pour obtenir uniquement le sommet de chaque groupe? Voir le pseudo-code
GetOnlyTheTop
ci - dessous:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Si une telle fonction n'existe pas, existe-t-il un moyen d'obtenir la sortie souhaitée?
- Ou en premier lieu, cela pourrait-il être causé par une base de données non normalisée? Je pense, puisque ce que je recherche, c'est juste une rangée, si cela
status
également être situé dans la table parent?
Veuillez consulter le tableau parent pour plus d'informations:
Documents
Tableau actuel
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
La table parent doit-elle être ainsi pour que je puisse facilement accéder à son état?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
MISE À JOUR Je viens d'apprendre à utiliser "appliquer", ce qui facilite la résolution de ces problèmes.
Réponses:
Si vous vous attendez à 2 entrées par jour, cela en choisira une arbitrairement. Pour obtenir les deux entrées pour une journée, utilisez plutôt DENSE_RANK
Quant à normalisé ou non, cela dépend si vous souhaitez:
En l'état, vous conservez l'historique des statuts. Si vous souhaitez également le dernier état dans la table parent (qui est la dénormalisation), vous aurez besoin d'un déclencheur pour maintenir le "statut" dans le parent. ou supprimez cette table d'historique d'état.
la source
Partition By
?With
est nouveau pour moi aussi :( J'utilise quand même mssql 2005.ROW_NUMBER
une sorte de sous-requête pour chaque ligne?Je viens d'apprendre à utiliser
cross apply
. Voici comment l'utiliser dans ce scénario:la source
J'ai fait quelques synchronisations sur les différentes recommandations ici, et les résultats dépendent vraiment de la taille de la table impliquée, mais la solution la plus cohérente utilise le CROSS APPLY Ces tests ont été exécutés contre SQL Server 2008-R2, en utilisant une table avec 6 500 enregistrements et un autre (schéma identique) avec 137 millions d'enregistrements. Les colonnes interrogées font partie de la clé primaire de la table et la largeur de la table est très petite (environ 30 octets). Les heures sont signalées par SQL Server à partir du plan d'exécution réel.
Je pense que la chose vraiment étonnante était la cohérence du temps pour le CROSS APPLY quel que soit le nombre de lignes impliquées.
la source
Je sais que c'est un vieux fil de discussion, mais les
TOP 1 WITH TIES
solutions sont assez agréables et pourraient être utiles à la lecture des solutions.Pour en savoir plus sur la clause TOP, cliquez ici .
la source
Si vous êtes préoccupé par les performances, vous pouvez également le faire avec MAX ():
ROW_NUMBER () nécessite une sorte de toutes les lignes de votre instruction SELECT, contrairement à MAX. Devrait accélérer considérablement votre requête.
la source
row_number()
même avec une indexation appropriée. Je le trouve particulièrement utile dans les scénarios d'auto-jointure. La chose à savoir cependant, c'est que cette méthode produira souvent un nombre plus élevé de lectures logiques et de décomptes, malgré le rapport d'un faible coût de sous-arbre. Vous devrez peser les coûts / avantages dans votre cas particulier pour déterminer s'il est réellement meilleur.Quel serveur de base de données? Ce code ne fonctionne pas sur tous.
En ce qui concerne la seconde moitié de votre question, il me semble raisonnable d'inclure le statut en colonne. Tu peux partir
DocumentStatusLogs
un journal, mais toujours stocker les dernières informations dans le tableau principal.BTW, si vous avez déjà la
DateCreated
colonne dans le tableau Documents, vous pouvez simplement rejoindre enDocumentStatusLogs
utilisant cela (tant qu'ilDateCreated
est unique dansDocumentStatusLogs
).Edit: MsSQL ne prend pas en charge USING, alors changez-le en:
la source
max(DateCreated)
C'est l'une des questions les plus faciles à trouver sur le sujet, je voulais donc donner une réponse moderne à cela (à la fois pour ma référence et pour aider les autres). En utilisant
first_value
etover
vous pouvez utiliser rapidement la requête ci-dessus:Cela devrait fonctionner dans Sql Server 2008 et versions ultérieures.
First_value
peut être considéré comme un moyen d'accomplirSelect Top 1
lors de l'utilisation d'uneover
clause.Over
permet le regroupement dans la liste de sélection, donc au lieu d'écrire des sous-requêtes imbriquées (comme le font la plupart des réponses existantes), cela le fait de manière plus lisible. J'espère que cela t'aides.la source
C'est un fil assez ancien, mais je pensais que je mettrais mes deux cents de la même manière que la réponse acceptée ne fonctionnait pas particulièrement bien pour moi. J'ai essayé la solution de gbn sur un grand ensemble de données et l'ai trouvée terriblement lente (> 45 secondes sur plus de 5 millions d'enregistrements dans SQL Server 2012). En regardant le plan d'exécution, il est évident que le problème est qu'il nécessite une opération SORT qui ralentit considérablement les choses.
Voici une alternative que j'ai retirée du cadre d'entité qui n'a besoin d'aucune opération SORT et effectue une recherche d'index NON clusterisé. Cela réduit le temps d'exécution à <2 secondes sur le jeu d'enregistrements susmentionné.
Maintenant, je suppose que quelque chose qui n'est pas entièrement spécifié dans la question d'origine, mais si la conception de votre table est telle que votre colonne ID est un ID d'incrémentation automatique et que DateCreated est défini sur la date actuelle à chaque insertion, puis même sans exécuter ma requête ci-dessus, vous pouvez réellement obtenir une augmentation considérable des performances de la solution de gbn (environ la moitié du temps d'exécution) simplement en commandant sur ID au lieu de commander sur DateCreated car cela fournira un ordre de tri identique et c'est un tri plus rapide.
la source
Mon code pour sélectionner le top 1 de chaque groupe
la source
Vérification de la réponse impressionnante et correcte de Clint ci-dessus:
Les performances entre les deux requêtes ci-dessous sont intéressantes. 52% étant le premier. Et 48% étant le deuxième. Une amélioration de 4% des performances en utilisant DISTINCT au lieu de COMMANDER PAR. Mais ORDER BY a l'avantage de trier par plusieurs colonnes.
Option 1:
Option 2:
Management Studio de M $: Après avoir mis en surbrillance et exécuté le premier bloc, mettez en surbrillance les options 1 et 2, clic droit -> [Afficher le plan d'exécution estimé]. Ensuite, exécutez le tout pour voir les résultats.
Résultats de l'option 1:
Résultats de l'option 2:
Remarque:
J'évite également les sous-requêtes EXISTS / IN dans la clause WHERE ou ON, car cela m'a causé des plans d'exécution terribles. Mais le kilométrage varie. Passez en revue le plan d'exécution et les performances du profil où et quand vous en avez besoin!
la source
Cette solution peut être utilisée pour obtenir les TOP N lignes les plus récentes pour chaque partition (dans l'exemple, N est 1 dans l'instruction WHERE et la partition est doc_id):
la source
Si vous souhaitez renvoyer uniquement l'ordre des documents récents par DateCreated, il ne renverra que le premier document par DocumentID
la source
CROSS APPLY
était la méthode que j'ai utilisée pour ma solution, car elle fonctionnait pour moi et pour les besoins de mes clients. Et d'après ce que j'ai lu, devrait fournir les meilleures performances globales si leur base de données se développait considérablement.la source
Voici 3 approches distinctes du problème en cours ainsi que les meilleurs choix d'indexation pour chacune de ces requêtes (veuillez essayer les index vous-même et voir la lecture logique, le temps écoulé, le plan d'exécution. J'ai fourni les suggestions de mon expérience sur de telles requêtes sans exécuter pour ce problème spécifique).
Approche 1 : utilisation de ROW_NUMBER (). Si l'index rowstore n'est pas en mesure d'améliorer les performances, vous pouvez essayer l'index columnstore non clusterisé / en cluster comme pour les requêtes avec agrégation et regroupement et pour les tables qui sont classées par différentes colonnes à tout moment, l'index columnstore est généralement le meilleur choix.
Approche 2 : utilisation de FIRST_VALUE. Si l'index du magasin de lignes n'est pas en mesure d'améliorer les performances, vous pouvez essayer un index de magasin de colonnes non clusterisé / en cluster comme pour les requêtes avec agrégation et regroupement et pour les tables qui sont toujours triées dans différentes colonnes, l'index de magasin de colonnes est généralement le meilleur choix.
Approche 3 : Utilisation de CROSS APPLY. La création d'un index de magasin de lignes sur la table DocumentStatusLogs couvrant les colonnes utilisées dans la requête devrait être suffisante pour couvrir la requête sans avoir besoin d'un index de magasin de colonnes.
la source
Je crois que cela peut être fait comme ça. Cela pourrait nécessiter quelques ajustements, mais vous pouvez simplement sélectionner le maximum dans le groupe.
Ces réponses sont exagérées ..
la source
Dans les scénarios où vous souhaitez éviter d'utiliser row_count (), vous pouvez également utiliser une jointure gauche:
Pour l'exemple de schéma, vous pouvez également utiliser un "pas dans la sous-requête", qui compile généralement vers la même sortie que la jointure gauche:
Notez que le modèle de sous-requête ne fonctionnerait pas si la table n'avait pas au moins une clé / contrainte / index unique à une seule colonne, dans ce cas la clé primaire "Id".
Ces deux requêtes ont tendance à être plus "coûteuses" que la requête row_count () (telle que mesurée par l'Analyseur de requêtes). Cependant, vous pouvez rencontrer des scénarios où ils renvoient des résultats plus rapidement ou activent d'autres optimisations.
la source
la source
Essaye ça:
la source
C'est le TSQL le plus vanille que je puisse trouver
la source
Il est vérifié dans SQLite que vous pouvez utiliser la requête simple suivante avec GROUP BY
Ici, MAX aide à obtenir la DateCreated maximale chaque groupe.
Mais il semble que MYSQL n'associe pas * -columns à la valeur de max DateCreated :(
la source