Selon MSDN , Median n'est pas disponible en tant que fonction d'agrégation dans Transact-SQL. Cependant, je voudrais savoir s'il est possible de créer cette fonctionnalité (en utilisant la fonction Créer un agrégat , une fonction définie par l'utilisateur ou une autre méthode).
Quelle serait la meilleure façon (si possible) de le faire - permettre le calcul d'une valeur médiane (en supposant un type de données numérique) dans une requête agrégée?
sql
sql-server
aggregate-functions
median
Yaakov Ellis
la source
la source
Réponses:
MISE À JOUR 2019: Au cours des 10 années écoulées depuis que j'ai écrit cette réponse, plus de solutions ont été découvertes qui pourraient donner de meilleurs résultats. De plus, les versions de SQL Server depuis lors (en particulier SQL 2012) ont introduit de nouvelles fonctionnalités T-SQL qui peuvent être utilisées pour calculer les médianes. Les versions de SQL Server ont également amélioré son optimiseur de requêtes, ce qui peut affecter les performances de diverses solutions médianes. Net-net, mon article d'origine de 2009 est toujours correct, mais il peut y avoir de meilleures solutions pour les applications SQL Server modernes. Jetez un œil à cet article de 2012 qui est une excellente ressource: https://sqlperformance.com/2012/08/t-sql-queries/median
Cet article a constaté que le modèle suivant est beaucoup, beaucoup plus rapide que toutes les autres alternatives, au moins sur le schéma simple qu'ils ont testé. Cette solution était 373 fois plus rapide (!!!) que la
PERCENTILE_CONT
solution la plus lente ( ) testée. Notez que cette astuce nécessite deux requêtes distinctes qui peuvent ne pas être pratiques dans tous les cas. Il nécessite également SQL 2012 ou une version ultérieure.Bien sûr, juste parce qu'un test sur un schéma en 2012 a donné d'excellents résultats, votre kilométrage peut varier, surtout si vous utilisez SQL Server 2014 ou une version ultérieure. Si la perf est importante pour votre calcul médian, je vous suggère fortement d'essayer et de tester plusieurs des options recommandées dans cet article pour vous assurer que vous avez trouvé la meilleure pour votre schéma.
Je serais également particulièrement prudent en utilisant la fonction (nouvelle dans SQL Server 2012)
PERCENTILE_CONT
recommandée dans l'une des autres réponses à cette question, car l'article lié ci-dessus a trouvé que cette fonction intégrée était 373 fois plus lente que la solution la plus rapide. Il est possible que cette disparité se soit améliorée depuis 7 ans, mais personnellement, je n'utiliserais pas cette fonction sur une grande table avant d'avoir vérifié ses performances par rapport à d'autres solutions.L'ORIGINAL 2009 POST EST CI-DESSOUS:
Il existe de nombreuses façons de le faire, avec des performances très variables. Voici une solution particulièrement bien optimisée, à partir des médianes, des ROW_NUMBERs et des performances . Il s'agit d'une solution particulièrement optimale en ce qui concerne les E / S réelles générées lors de l'exécution - elle semble plus coûteuse que les autres solutions, mais elle est en fait beaucoup plus rapide.
Cette page contient également une discussion sur d'autres solutions et des détails sur les tests de performances. Notez l'utilisation d'une colonne unique comme désambiguïsateur dans le cas où plusieurs lignes ont la même valeur que la colonne médiane.
Comme pour tous les scénarios de performances de base de données, essayez toujours de tester une solution avec des données réelles sur du matériel réel - vous ne savez jamais quand une modification de l'optimiseur de SQL Server ou une particularité de votre environnement rendra une solution normalement rapide plus lente.
la source
Si vous utilisez SQL 2005 ou mieux, il s'agit d'un calcul médian simple et simple pour une seule colonne d'une table:
la source
select gid, median(score) from T group by gid
. Avez-vous besoin d'une sous-requête corrélée pour cela?Dans SQL Server 2012, vous devez utiliser PERCENTILE_CONT :
Voir également: http://blog.sqlauthority.com/2011/11/20/sql-server-introduction-to-percentile_cont-analytic-functions-introduced-in-sql-server-2012/
la source
DISTINCT
ouGROUPY BY SalesOrderID
? Sinon, vous aurez beaucoup de lignes en double.PERCENTILE_DISC
Ma réponse rapide d'origine était:
Cela vous donnera la plage médiane et interquartile d'un seul coup. Si vous ne voulez vraiment qu'une seule ligne qui est la médiane, décommentez la clause where.
Lorsque vous collez cela dans un plan d'explication, 60% du travail consiste à trier les données, ce qui est inévitable lors du calcul de statistiques dépendantes de la position comme celle-ci.
J'ai modifié la réponse pour suivre l'excellente suggestion de Robert Ševčík-Robajz dans les commentaires ci-dessous:
Cela devrait calculer les valeurs médiane et centile correctes lorsque vous avez un nombre pair d'éléments de données. Encore une fois, décommentez la clause where finale si vous voulez uniquement la médiane et non la distribution centile entière.
la source
Encore mieux:
Du maître lui-même, Itzik Ben-Gan !
la source
MS SQL Server 2012 (et versions ultérieures) possède la fonction PERCENTILE_DISC qui calcule un centile spécifique pour les valeurs triées. PERCENTILE_DISC (0,5) calculera la médiane - https://msdn.microsoft.com/en-us/library/hh231327.aspx
la source
Simple, rapide, précis
la source
Si vous souhaitez utiliser la fonction Créer un agrégat dans SQL Server, voici comment procéder. Le faire de cette façon a l'avantage de pouvoir écrire des requêtes propres. Notez que ce processus pourrait être adapté pour calculer une valeur de centile assez facilement.
Créez un nouveau projet Visual Studio et définissez le framework cible sur .NET 3.5 (c'est pour SQL 2008, il peut être différent dans SQL 2012). Créez ensuite un fichier de classe et insérez le code suivant, ou l'équivalent c #:
Ensuite, compilez-le et copiez le fichier DLL et PDB sur votre machine SQL Server et exécutez la commande suivante dans SQL Server:
Vous pouvez ensuite écrire une requête pour calculer la médiane comme ceci: SELECT dbo.Median (Field) FROM Table
la source
Je suis juste tombé sur cette page en cherchant une solution basée sur un ensemble de médiane. Après avoir examiné certaines des solutions ici, j'ai trouvé ce qui suit. L'espoir est aide / fonctionne.
la source
La requête suivante renvoie la médiane d'une liste de valeurs dans une colonne. Il ne peut pas être utilisé en tant que ou avec une fonction d'agrégation, mais vous pouvez toujours l'utiliser comme sous-requête avec une clause WHERE dans la sélection interne.
SQL Server 2005+:
la source
Bien que la solution de Justin Grant semble solide, j'ai trouvé que lorsque vous avez un certain nombre de valeurs en double dans une clé de partition donnée, les numéros de ligne pour les valeurs en double ASC finissent dans le désordre afin qu'ils ne s'alignent pas correctement.
Voici un fragment de mon résultat:
J'ai utilisé le code de Justin comme base de cette solution. Bien qu'il ne soit pas aussi efficace compte tenu de l'utilisation de plusieurs tables dérivées, il résout le problème de classement des lignes que j'ai rencontré. Toute amélioration serait la bienvenue car je ne suis pas très expérimenté en T-SQL.
la source
L'exemple de Justin ci-dessus est très bon. Mais ce besoin de clé primaire doit être énoncé très clairement. J'ai vu ce code dans la nature sans la clé et les résultats sont mauvais.
La plainte que je reçois au sujet du Percentile_Cont est qu'il ne vous donnera pas une valeur réelle de l'ensemble de données. Pour obtenir une "médiane" qui est une valeur réelle de l'ensemble de données, utilisez Percentile_Disc.
la source
Dans un UDF, écrivez:
la source
Constatation médiane
Il s'agit de la méthode la plus simple pour trouver la médiane d'un attribut.
la source
Voir d'autres solutions pour le calcul de la médiane en SQL ici: " Un moyen simple de calculer la médiane avec MySQL " (les solutions sont pour la plupart indépendantes du fournisseur).
la source
Pour une variable / mesure continue 'col1' de 'table1'
la source
En utilisant l'agrégat COUNT, vous pouvez d'abord compter le nombre de lignes et les stocker dans une variable appelée @cnt. Ensuite, vous pouvez calculer les paramètres du filtre OFFSET-FETCH pour spécifier, en fonction de l'ordre de quantité, le nombre de lignes à ignorer (valeur de décalage) et le nombre de lignes à filtrer (valeur de récupération).
Le nombre de lignes à ignorer est (@cnt - 1) / 2. Il est clair que pour un nombre impair, ce calcul est correct car vous soustrayez d'abord 1 pour la valeur intermédiaire unique, avant de diviser par 2.
Cela fonctionne également correctement pour un nombre pair car la division utilisée dans l'expression est une division entière; donc, lorsque vous soustrayez 1 d'un nombre pair, vous vous retrouvez avec une valeur impaire.
Lors de la division de cette valeur impaire par 2, la partie fraction du résultat (0,5) est tronquée. Le nombre de lignes à récupérer est de 2 - (@cnt% 2). L'idée est que lorsque le nombre est impair, le résultat de l'opération modulo est 1, et vous devez récupérer 1 ligne. Lorsque le nombre est égal, le résultat de l'opération modulo est 0 et vous devez récupérer 2 lignes. En soustrayant le résultat 1 ou 0 de l'opération modulo de 2, vous obtenez respectivement le 1 ou le 2 souhaité. Enfin, pour calculer la quantité médiane, prenez une ou deux quantités de résultat et appliquez une moyenne après avoir converti la valeur entière d'entrée en une valeur numérique comme suit:
la source
Je voulais trouver une solution par moi-même, mais mon cerveau a trébuché et est tombé en chemin. Je pense que cela fonctionne, mais ne me demandez pas de l'expliquer le matin. : P
la source
la source
Cela fonctionne avec SQL 2000:
la source
Pour les débutants comme moi qui apprennent les bases, je trouve personnellement cet exemple plus facile à suivre, car il est plus facile de comprendre exactement ce qui se passe et d'où viennent les valeurs médianes ...
Dans la crainte absolue de certains des codes ci-dessus cependant !!!
la source
C'est une réponse aussi simple que j'ai pu trouver. A bien fonctionné avec mes données. Si vous souhaitez exclure certaines valeurs, ajoutez simplement une clause where à la sélection interne.
la source
La solution suivante fonctionne sous ces hypothèses:
Code:
la source
la source
J'essaie avec plusieurs alternatives, mais étant donné que mes enregistrements de données ont des valeurs répétées, les versions ROW_NUMBER ne semblent pas être un choix pour moi. Voici donc la requête que j'ai utilisée (une version avec NTILE):
la source
En s'appuyant sur la réponse de Jeff Atwood ci-dessus, c'est avec GROUP BY et une sous-requête corrélée pour obtenir la médiane de chaque groupe.
la source
Souvent, nous pouvons avoir besoin de calculer la médiane non seulement pour la table entière, mais pour les agrégats par rapport à certains ID. En d'autres termes, calculez la médiane de chaque ID dans notre tableau, où chaque ID a de nombreux enregistrements. (basé sur la solution éditée par @gdoron: bonnes performances et fonctionne dans de nombreux SQL)
J'espère que ça aide.
la source
Pour votre question, Jeff Atwood avait déjà donné la solution simple et efficace. Mais, si vous cherchez une autre approche pour calculer la médiane, le code SQL ci-dessous vous aidera.
Si vous cherchez à calculer la médiane dans MySQL, ce lien github sera utile.
la source
C'est la solution la plus optimale pour trouver des médianes auxquelles je puisse penser. Les noms dans l'exemple sont basés sur l'exemple de Justin. Assurez-vous qu'il existe un index pour la table Sales.SalesOrderHeader avec les colonnes d'index CustomerId et TotalDue dans cet ordre.
METTRE À JOUR
Je ne savais pas trop quelle méthode avait les meilleures performances, j'ai donc fait une comparaison entre ma méthode Justin Grants et Jeff Atwoods en exécutant une requête basée sur les trois méthodes dans un lot et le coût du lot de chaque requête était:
Sans index:
Et avec index
J'ai essayé de voir à quel point les requêtes évoluent si vous avez un index en créant plus de données à partir d'environ 14 000 lignes par un facteur de 2 à 512, ce qui signifie au final environ 7,2 millions de lignes. Remarque J'ai vérifié que le champ CustomeId était unique pour chaque fois que je faisais une seule copie, de sorte que la proportion de lignes par rapport à l'instance unique de CustomerId était maintenue constante. Pendant que je faisais cela, j'ai exécuté des exécutions où j'ai reconstruit l'index par la suite, et j'ai remarqué que les résultats se stabilisaient autour d'un facteur 128 avec les données que j'avais sur ces valeurs:
Je me demandais comment les performances auraient pu être affectées par la mise à l'échelle du nombre de lignes mais en maintenant une constante CustomerId constante, alors j'ai configuré un nouveau test où je l'ai fait. Maintenant, au lieu de se stabiliser, le rapport des coûts par lots a continué de diverger, également au lieu d'environ 20 lignes par CustomerId par moyenne que j'avais à la fin environ 10000 lignes par un ID unique. Les chiffres où:
Je me suis assuré d'avoir implémenté chaque méthode correctement en comparant les résultats. Ma conclusion est que la méthode que j'ai utilisée est généralement plus rapide tant que l'index existe. A également remarqué que cette méthode est ce qui est recommandé pour ce problème particulier dans cet article https://www.microsoftpressstore.com/articles/article.aspx?p=2314819&seqNum=5
Un moyen d'améliorer encore davantage les performances des appels ultérieurs à cette requête consiste à conserver les informations de comptage dans une table auxiliaire. Vous pouvez même le maintenir en ayant un déclencheur qui se met à jour et contient des informations concernant le nombre de lignes SalesOrderHeader dépendant de CustomerId, bien sûr, vous pouvez également simplement stocker la médiane.
la source
Pour les jeux de données à grande échelle, vous pouvez essayer ce GIST:
https://gist.github.com/chrisknoll/1b38761ce8c5016ec5b2
Il fonctionne en agrégeant les valeurs distinctes que vous trouveriez dans votre ensemble (telles que l'âge ou l'année de naissance, etc.) et utilise les fonctions de la fenêtre SQL pour localiser toute position de centile que vous spécifiez dans la requête.
la source