【问题标题】:Counting : multiple queries or one query with multiple subqueries?计数:多个查询还是一个带有多个子查询的查询?
【发布时间】:2011-12-06 08:58:42
【问题描述】:

我想做一个摘要脚本,比如今天、昨天、本周、上周、本月、上个月有多少新用户。

我正在使用 CodeIgniter,所以我想知道哪个更好?在这样的模型上使用多个调用:

$this->model->get_today_users();
$this->model->get_yesterday_users();
$this->model->get_this_week_users();
// so on

上述每个函数仅返回具有特定日期范围的用户数(例如 SELECT COUNT(*) WHERE join_date=NOW() for today_users 等 - 当然使用 CI 活动记录)。

或者最好使用一个包含所有必要子查询的单个查询,如下所示:

function stats(){
$this->db->select('*');
$this->db->select('(SELECT COUNT(*) FROM users WHERE join_date=NOW()) as today');
$this->db->select('(SELECT COUNT(*) FROM users WHERE join_date=NOW()-INTERVAL 1 DAY) as yesterday');
$this->db->select('(SELECT COUNT(*) FROM users WHERE WEEK(join_date)=WEEK(NOW()) ) as this_week');
$this->db->select('(SELECT COUNT(*) FROM users WHERE WEEK(join_date)=WEEK(NOW())-1 ) as last_week');
$this->db->select('(SELECT COUNT(*) FROM users WHERE MONTH(join_date)=MONTH(NOW()) ) as this_month');
$this->db->select('(SELECT COUNT(*) FROM users WHERE MONTH(join_date)=MONTH(NOW())-1 ) as last_month');
$this->db->from('users');
$ret = $this->db->get();
}

然后调用例如 $data['stat'] = $this->model->stats();所以在视图中,我可以调用 $stat->today、$stat->this_week 等。

提前感谢您的回答。

更新:如果我想创建一个接收日期的函数怎么办?假设我必须像这样循环一个月:

//for the sake of simplicity
foreach(day in a month as $r){
    $this->model->get_user_on_this_day($r);
}

这意味着一个月 30 天,它将循环 30 次!还有比这更好的方法吗?

【问题讨论】:

    标签: mysql codeigniter


    【解决方案1】:

    正确的答案可能是:视情况而定。从代码可重用性和可读性的角度来看,第一个选项可能更好。另一方面,第二个选项意味着您只联系数据库一次而不是三次。所以后者在性能方面可能会更好,因为这意味着您只联系数据库一次,这意味着只有一个连接、一个事务等。对于大型数据库、慢速连接等,这意味着更快的结果。

    【讨论】:

    • 是的,我也这么认为。但是说在未来,我必须每天/每周/每月添加统计信息,第二个将很难自定义,你不觉得吗?
    • 也许比其他选项难一点,是的。但是,您应该考虑进行此类更改的频率,以及这是否超过了在使代码更易于维护时获得的性能损失。
    • 同意虽然在性能方面可能不是最好的解决方案,但第一个选项绝对更易于阅读、维护和重用。
    【解决方案2】:

    IMO,这两种方法都没有经过优化。
    使用最少查询是最好的,因此,您应该执行如下所示的 GROUP BY 查询:

    select date_format(join_date, '%Y-%m-%d') as the_date, count(*) as total
    from users
    where join_date between $start AND $end   <-- the date range
    group by the_date;
    

    获得结果后,您可以使用日期作为键设置数组,
    然后你可以进一步按天、周、月分组...(随你喜欢)


    似乎 OP 不明白这是如何工作的:-

    上面的查询是一次性查询,会返回类似的行

    2011-12-09, 20 users join
    2011-12-08, 10 users join
    2011-12-07, 51 users join
    ...
    

    你所要做的就是迭代结果(非活动记录)

    $query = $this->db->query($sql, array($start, $end));
    $rtn   = array();
    $total = 0;
    foreach ($query->result() as $row)
    {
      $rtn[$row->the_date] = $row->total;
      $total += $row->total;
    }
    

    在循环结束时,$total = 从 $start 到 $end 注册的用户总数。

    比在月-日循环中执行 30(或 31)个查询更有效和优化。

    【讨论】:

    • 这也是一种可能,但这意味着您必须首先从数据库中获取所有数据,然后在您的代码中计数。如果您有一个大型数据库,则在数据库中进行计数然后检索结果可能会更快。这一切都取决于您的数据库的大小以及您想对数据做什么,这种方法是否比另一种更好。
    • 您不必查询所有行,只需查询与 $start 和 $end 匹配的行(无论如何您都需要)
    • AJ,确实如此,但大多数情况下,此查询返回的行可能仍然非常多。正如我所说,这不一定是坏事,这取决于。
    • 单个查询比多个查询便宜。你说的很多行是什么意思?以 $start、$end 为界(如果是两个月,可能是 60 天)。你真的认为这是很多吗?
    • AJ,这取决于网站每天获得多少新用户。像 SO 这样的网站将受益于数据库中的计数。许多其他网站也是如此。在这种特殊情况下?我不知道。
    【解决方案3】:

    您是否考虑过使用您提到的第二个选项在您的用户表上创建一个视图?您的查询将只有一个执行计划,并且仅当主表中的数据发生更改时才会处理它。而且您只需要一个简单的查询即可获得所需的信息。

    如果您需要添加更多计数,正如您所提到的,您只需在视图中添加列。

    【讨论】:

    • 但是由于它只是一个单一的查询,从模型或使用 MySQL 视图调用它有什么区别吗?
    • 是的,我想是的。仅当主表更改时才会重新生成视图。所以计数要少得多。如果你一遍又一遍地询问 db 相同的查询,数据库将不得不一遍又一遍地计算行数,并且它需要更多的数据库性能,而不是从视图中选择几个字段。
    【解决方案4】:

    一种方法是,能够在一个查询中使用您的第一个选项,这将有一个初始化命令。

    $this-&gt;model-&gt;buildmodel();

    您可以将一周、一个月、昨天 ETC 的记录保存在一个数组中,以便稍后调用,这意味着它可以轻松定制,而且速度更快,只需一次查询。

    【讨论】:

      【解决方案5】:

      您应该使用单独的查询并缓存给定日期的结果,因此您不必多次查询。

      由于过去人们无法加入,因此给定查询的数据(当天除外)将永远不会改变。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-24
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        相关资源
        最近更新 更多