2012-01-01 2 views
3

Comment est-il possible de renvoyer des lignes avec une somme cumulée pour une ligne supérieure ou inférieure à une valeur spécifiée?somme cumulée dans la requête

tableau:

id | count 
----------- 
1 | 30 
2 | 10 
3 | 5 
4 | 20 
5 | 15 

recherche:

SELECT id, count 
FROM table 
ORDER BY id 
HAVING SUM(count) < 50 

lignes de retour:

id | count 
------------- 
1 | 30 
2 | 10 
3 | 5 

mise à jour

code:

public function query(){ 
    switch($this->table){ 
     case 'in_stock': 
      return "SELECT * FROM ".Init::$static['db'].".stock 
       WHERE id<=dynaccount.stock_first_available_id(".$this->value['num_stock'].", ".$this->value['product_id'].", ".(isset($this->att_arr['gid']) ? $this->att_arr['gid']:$_SESSION['gid']).") 
       ORDER BY time, id"; 
    } 
} 

procédure:

DELIMITER $$ 

DROP FUNCTION IF EXISTS `stock_first_available_id` $$ 
CREATE DEFINER=`dynaccount`@`localhost` FUNCTION `stock_first_available_id`(_running_total_limit INT, _product_id INT, _group_id INT) RETURNS INT 
BEGIN 
    DECLARE _running_count INT default 0; 
    DECLARE _id INT; 
    DECLARE _current_id INT; 
    DECLARE _sum_count INT; 

    IF (SELECT COUNT(*) FROM stock WHERE group_id=_group_id && type=2 && product_id=_product_id) = 0 THEN 
     RETURN 0; 
    END IF; 

    DECLARE _cur CURSOR FOR SELECT id, count FROM stock WHERE group_id=_group_id && type=2 && product_id=_product_id ORDER BY time DESC, id DESC; 

    OPEN _cur; 

    read_loop: LOOP 
     FETCH _cur INTO _id, _sum_count; 

     SET _running_count = _running_count + _sum_count; 
     SET _current_id = _id; 

     IF _running_count > _running_total_limit THEN 
      LEAVE read_loop; 
     END IF; 
    END LOOP read_loop; 

    CLOSE _cur; 

    RETURN _current_id; 
END $$ 

DELIMITER ; 

erreur:

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE _cur CURSOR FOR SELECT id, count FROM stock WHERE group_id=_group_id &amp;&amp; ' at line 12 
+0

MySQL est votre seule option? –

+0

@mu, que voulez-vous dire? n'est-il pas possible de faire dans mysql? – clarkk

+0

Jetez un oeil à http://stackoverflow.com/questions/1135627/mysql-select-accumulated-column. Vous pouvez alors sélectionner toutes les lignes où la somme accumulée est inférieure au total. – dash

Répondre

6

La requête suivante:

SELECT * FROM 
(SELECT id, 
     count, 
     @running_count := @running_count + count AS Counter 
    FROM sumtest, (SELECT @running_count := 0) AS T1 ORDER BY id) AS TableCount 

WHERE TableCount.Counter < 50; 

produit les résultats:

id count Counter 
1 30  30 
2 10  40 
3 5  45 

J'ai copié votre table dans MySql et l'ai appelée "sumtest" btw. S'il vous plaît remplacer avec votre nom de table.

En fait, nous calculons le total cumulé, dans l'ordre des identifiants, puis l'utilisons comme sous-requête.

donc cette requête:

SELECT id, 
     count, 
     @running_count := @running_count + count AS Counter 
FROM sumtest, (SELECT @running_count := 0) AS T1 
ORDER BY id 

Produit:

id count Counter 
1 30  30 
2 10  40 
3 5  45 
4 20  65 
5 15  80 

Alors il devient une question triviale pour sélectionner toutes les lignes où le compteur est inférieur à votre somme désirée en effectuant une autre sélectionnez sur ce.

EDIT: Voici un exemple avec un curseur. Je viens jeté cette fonction ainsi que pour vous (note ma table est appelée sumtest et mon compte est la racine par défaut @ localhost):

DELIMITER $$ 

DROP FUNCTION IF EXISTS `Test_Cursing` $$ 
CREATE DEFINER=`root`@`localhost` FUNCTION `Test_Cursing`(_running_total_limit INT) RETURNS int 
BEGIN 
    /* Why am I on StackOverflow at 01:41 on New Years Day. Dear oh dear, where's the beer? */ 
    DECLARE _running_count INT default 0; 
    DECLARE _id INT; 
    DECLARE _current_id INT; 
    DECLARE _sum_count INT; 

    DECLARE _cur CURSOR FOR SELECT id, count FROM sumtest ORDER BY id; 

    OPEN _cur; 

    read_loop: LOOP 
    FETCH _cur INTO _id, _sum_count; 

    SET _running_count = _running_count + _sum_count; 

    IF _running_count > _running_total_limit THEN 
     LEAVE read_loop; 
    END IF; 

    SET _current_id = _id; 

    END LOOP; 

    CLOSE _cur; 

    RETURN _current_id; 

END $$ 

DELIMITER ; 

Appeler cette façon:

SELECT Test_Cursing(50); 

retournera id = 3 - c'est-à-dire, le dernier identifiant avant que la limite totale soit violée. Vous pouvez ensuite utiliser pour:

SELECT * FROM sumtest WHERE id <= Test_Cursing(50); 

qui retourne:

id count 
1 30 
2 10 
3 5 
+0

ne l'ai pas testé, mais ne serait-il pas inefficace avec toutes les sélections? il finira avec 3 sélectionne – clarkk

+0

Depends. Si la table est très, très grande, vous pouvez voir une perte de performance du total cumulé, mais vous devrez tester (et utiliser le mot clé EXPLAIN par exemple). Le select @running_count: = 0 et le select externe sont triviaux et ne devraient avoir aucun impact en comparaison. D'autre part, vous pouvez utiliser un curseur que vous pouvez quitter dès que vous atteignez votre total désiré. C'est aussi trivial et efficace de faire aussi du code avec un SqlDataReader (si vous connaissez C#?) – dash

+0

ok, merci :) mais comment pouvez-vous intégrer un curseur dans la requête afin qu'il sorte dès que la valeur est atteinte? – clarkk

Questions connexes