【问题标题】:accumulated sum in query查询中的累计和
【发布时间】:2012-01-01 00:17:06
【问题描述】:

对于大于或小于指定值的行,如何返回累积和的行?

表:

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 

【问题讨论】:

  • @mu,你是什么意思?是不是可以在mysql中做?
  • 看看stackoverflow.com/questions/1135627/…。然后,您可以选择累计总和小于总数的所有行。
  • 在 SQL 中计算大量行的运行总计通常效率低下(除非它们支持完整的 OVER 子句)。我觉得可能有一种方法可以在 MySQL 中使用用户变量来合理有效地完成此要求。
  • @martin,你是什么意思?你能举一个OVER子句的例子吗?
  • @MartinSmith 是的,当然,MySql 不支持 over 子句真的很遗憾,因为它使这变得微不足道。否则你需要一个运行总数或(urghh)一个光标,我想。顺便说一句,祝 0b11111011100 一切顺利!

标签: mysql cumulative-sum


【解决方案1】:

以下查询:

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

因此,通过对其执行另一次选择来选择计数器小于所需总和的所有行就变得微不足道了。

编辑:这是一个带有光标的示例。我刚刚为你整理了这个函数(注意我的表叫做 sumtest,我的帐户是默认的 root@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 ;

这样称呼它:

SELECT Test_Cursing(50);

将返回 id = 3 - 即超出运行总数限制之前的最后一个 id。然后您可以使用它来:

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

返回:

id  count
1   30
2   10
3   5

【讨论】:

  • 还没有测试过,但不是所有的选择都没有效率吗?它将以 3 次选择结束
  • 视情况而定。如果表非常非常大,那么您可能会从运行总数中看到性能损失,但您需要进行测试(例如,使用 EXPLAIN 关键字)。选择 @running_count:=0 和外部选择是微不足道的,相比之下应该没有任何影响。另一方面,您可以使用光标,因为您可以在达到所需总数后立即退出。使用 SqlDataReader 编写代码也很简单且高效(如果您了解 C#?)
  • 好的,谢谢 :) 但是如何将光标集成到查询中,以便在达到值后立即退出?
  • 是否可以在选择行后进行“检查”,以便在达到值后也能获得第一行? :)
  • @clarkk 我已经用光标更新了。这将在您的运行总目标被破坏之前返回该行的 ID。这应该更有效率。让我在上午 1 点 30 分写一个光标... Grrrrrr :-) 希望这会有所帮助,2012 年一切顺利。
猜你喜欢
  • 1970-01-01
  • 2021-12-28
  • 2012-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多