2012-01-18 9 views
5

Je veux écrire une fonction qui calcule un calcul de moyenne tronquée simple dans MySQL. La fonction sera (évidemment) une fonction agrégée. Je suis nouveau à écrire des fonctions etc dans MySQL, donc je pourrais faire avec de l'aide.Calcul de moyenne tronquée en MySQL

L'algorithme de la moyenne tronquée sera le suivant (pseudo-code):

CREATE AGGREGATE FUNCTION trimmed_mean(elements DOUBLE[], trim_size INTEGER) 
RETURNS DOUBLE 
BEGIN 
    -- determine number of elements 
    -- ensure that number of elements is greater than 2 * trim_size else return error 
    -- order elements in ASC order 
    -- chop off smallest trim_size elements and largest trim_size elements 
    -- calculate arithmetic average of the remaining elements 
    -- return arithmetic average 
END 

Quelqu'un peut-il aider à la façon d'écrire la fonction ci-dessus correctement, pour une utilisation avec MySQL?

+2

Y at-il un raison spécifique que vous voulez faire cela en tant que fonction, plutôt que comme une requête? De plus, étant donné que vous avez préféré une réponse à une question précédente parce qu'elle utilisait le langage SQL standard, devrez-vous l'utiliser sur plusieurs SGBDR différents (pas seulement MySQL)? –

+0

@MarkBannister J'avais l'intention de travailler avec PG (mon db préféré!), Mais j'ai dû passer par trop de cerceaux pour que PG fonctionne avec PHP (recompiler PHP [ou des questions folles similaires] etc), donc j'ai opté pour mySQL J'ai déjà travaillé avec PHP. La raison pour laquelle je le voulais en tant que fonction est que je veux retourner une moyenne tronquée en tant que colonne dans une requête. Je suppose (si j'avais une solution SQL), je pourrais bidouiller un peu de SQL pour "coller" les valeurs moyennes rognées comme une colonne à mon ensemble de données retourné. –

+0

@MarkBannister: réponse courte à votre question. Une version SQL ANSI serait idéale. Mais comme il m'arrive de travailler avec mySQL, alors si je dois être db-centric, un SQL à saveur MySQL aura la priorité. –

Répondre

1

C'est pas une mince tâche, vous devez écrire dans c/C++ ...


Une option dans MySQL lui-même, est d'écrire une fonction de vue ou scalaire qui agrège les données comme vous le souhaitez, mais à partir d'une table spécifique. Cela restreint évidemment la fonction à une seule table source, ce qui peut ne pas être idéal.

Une façon de contourner cela pourrait être d'avoir une table dédiée à cette fonction ...

  • démarrer une transaction
  • clairement la table
  • insérer vos données d'échantillon
  • requête du Vue/Fonction

(Ou quelque chose de similaire)

Ceci exclut les variations GROUP BY, sauf si vous utilisez des paramètres dynamiques sql ou pass pour votre fonction pour des modèles de regroupement spécifiques.

Tout est moins qu'idéal, désolé.

+0

Cela ne me dérange pas de descendre la route C/C++ (comme un dernier recours) - mais je préfère ne pas le faire, simplement parce que je ne veux pas passer des heures à me familiariser avec les types de données internes mySQL, etc. S'il existe un exemple de 'hello world' pour un agrégat écrit en C/C++, serait un très bon point de départ (en réduisant la courbe d'apprentissage), puisque ce que je veux faire, est relativement trivial (la partie de l'algorithme qui est). –

+0

Dans le lien de Dems: * "La distribution des sources de MySQL inclut un fichier sql/udf_example.c qui définit 5 nouvelles fonctions." * –

1

Jetez un oeil à cet exemple (pour MySQL) -

Créer table de test:

CREATE TABLE test_table (
    id INT(11) NOT NULL AUTO_INCREMENT, 
    value INT(11) DEFAULT NULL, 
    PRIMARY KEY (id) 
); 

INSERT INTO test_table(value) VALUES 
    (10), (2), (3), (5), (4), (7), (1), (9), (3), (5), (9); 

Calculons valeur moyenne (variante modifiée):

SET @trim_size = 3; 

SELECT AVG(value) avg FROM (
    SELECT value, @pos:[email protected] + 1 pos FROM (SELECT * FROM test_table ORDER BY value) t1, (SELECT @pos:=0) t2 
) t 
WHERE pos > @trim_size AND pos <= @pos - @trim_size; 

+--------+ 
| avg | 
+--------+ 
| 4.8000 | 
+--------+ 
+0

Sûrement il devrait être 4.8 - le trim devrait éliminer un mais pas les deux? c'est-à-dire: (pas (1 + 2 + 3) + 3 + 4 + 5 + 5 + 7 + pas (9 + 9 + 10))/5 –

+0

@Mark Bannister Vous avez raison. J'ai manqué les éléments de point de commande dans l'ordre ASC. Il devrait être ordonné par le champ 'value'. J'ai modifié la requête. Merci;-) – Devart