2012-01-01 2 views
3

Как можно возвращать строки с суммой накоплений для строки большего или меньшего, чем указанное значение?накопленная сумма в запросе

стол:

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

запрос:

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

возвращаемые строки:

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

обновление

код:

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"; 
    } 
} 

процедура:

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 ; 

ошибка:

#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 единственный вариант? –

+0

@mu, что вы имеете в виду? не возможно ли в mysql? – clarkk

+0

Взгляните на http://stackoverflow.com/questions/1135627/mysql-select-accumulated-column. Затем вы можете выбрать все строки, где накопленная сумма меньше общей. – dash

ответ

6

следующий запрос:

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; 

производит результаты:

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

Я скопировал ваш стол в MySql и назвал его «sumtest» кстати. Пожалуйста, замените имя своей таблицы.

Эффективно, мы определяем текущее общее количество в порядке id, а затем используем его как подзапрос.

Так этот запрос:

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

Производит:

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

Так тогда это становится тривиальным, чтобы выбрать все те строки, в которых счетчик меньше нужной суммы, выполняя другой выбор на это.

EDIT: Вот пример с помощью курсора. Я только что бросил эту функцию вместе для вас (обратите внимание на мой стол называется sumtest и мой счет по умолчанию корень @ локальный):

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 ; 

Вызов это таким образом:

SELECT Test_Cursing(50); 

будет возвращать идентификатор = 3 - то есть последний id до того, как будет пробит текущий лимит. Затем вы можете использовать это:

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

который возвращает:

id count 
1 30 
2 10 
3 5 
+0

не проверили его, но разве это не было бы неэффективно со всеми выборами? в итоге будет 3 выбора – clarkk

+0

Зависит. Если таблица очень, очень большая, то вы можете увидеть потерю производительности из общей суммы, но вам нужно будет протестировать (и, например, использовать ключевое слово EXPLAIN). Выбор @running_count: = 0 и внешний выбор тривиальны и не должны иметь никакого влияния в сравнении. С другой стороны, вы можете использовать курсор, так как вы можете выйти, как только достигнете желаемого результата. Это также тривиально и эффективно делать в коде, а также с помощью SqlDataReader (если вы знаете C#?) – dash

+0

нормально, спасибо :), но как бы вы могли интегрировать курсор в запрос, чтобы он выходил, как только это значение было достигнуто? – clarkk