0

我需要计算来自不同(!)表的行数并将结果保存为某种统计信息。该脚本非常简单并且按预期工作,但我想知道是否最好使用带有(在这种情况下)8个子查询的单个查询,或者我是否应该使用单独的8个查询,或者是否有更好、更快和更多的查询先进的解决方案...

我将 MySQLi 与准备好的语句一起使用,因此单个查询可能如下所示:

$sql = 'SELECT
            (SELECT COUNT(cat1_id) FROM `cat1`),
            (SELECT COUNT(cat2_id) FROM `cat2`),
            (SELECT COUNT(cat2_id) FROM `cat2` WHERE `date` >= DATE(NOW())),
            (SELECT COUNT(cat3_id) FROM `cat3`),
            (SELECT COUNT(cat4_id) FROM `cat4`),
            (SELECT COUNT(cat5_id) FROM `cat5`),
            (SELECT COUNT(cat6_id) FROM `cat6`),
            (SELECT COUNT(cat7_id) FROM `cat7`)';

$stmt = $db->prepare($sql);
$stmt->execute();
$stmt->bind_result($var1, $var2, $var3, $var4, $var5, $var6, $var7, $var8);
$stmt->fetch();
$stmt->free_result();
$stmt->close();

而单独的查询看起来像这样(x 8):

$sql = 'SELECT
            COUNT(cat1_id)
        FROM
            `cat1`';

$stmt = $db->prepare($sql);
$stmt->execute();
$stmt->bind_result($var1);
$stmt->fetch();
$stmt->free_result();
$stmt->close();

所以,与这种查询相关的更快或“更好的风格”(例如统计信息,计数器..)

4

4 回答 4

0

我的倾向是尽可能将查询放入 FROM 而不是 SELECT 中。在此示例中,它需要表之间的交叉连接:

select c1.val, c2.val . . .
from (select count(cat1_id) as val from cat1) c1 cross join
     (select count(cat2_id as val from cat2) c2 cross join
     . . .

性能应该是一样的。但是,优势出现在您的 cat2 表中:

select c1.val, c2.val, c2.valnow, . . .
from (select count(cat1_id) as val from cat1) c1 cross join
     (select count(cat2_id) as val
             count(case when date >= date(now()) then cat2_id end)
      from cat2
     ) c2 cross join
     . . .

您无需扫描表两次即可获得两个值,从而真正节省了成本。当您意识到您可能想要修改查询以返回多个值时,这也很有帮助。

我相信交叉连接和 select-within-select 将具有相同的性能特征。真正确定的唯一方法是测试不同的版本。

于 2012-08-21T01:33:33.140 回答
-1

只是为了跟进您的评论,这里是使用我的一个数据库的示例。在这里使用准备好的声明不会给你带来任何好处。这个多重查询实际上只对 D/B 引擎执行一次 RPC。所有其他调用都是 PHP 运行时系统的本地调用。

$db = new mysqli('localhost', 'user', 'password', 'blog');
$table  = explode( ' ', 'articles banned comments config language members messages photo_albums photos');
foreach( $table as $t ) {
   $sql[] = "select count(*) as count from blog_$t";
}
if ($db->multi_query( implode(';',$sql) )) {
  foreach( $table as $t ) {
    if  ( ($rs  = $db->store_result() ) &&
          ($row = $rs->fetch_row()    ) ) {
       $result[$t] = $row[0];
       $rs->free();
       $db->next_result(); // you must execute one per result set
    }
  }
}
$db->close();
var_dump( $result );

只是出于兴趣,我对此做了一个strace相关的四行是

16:54:09.894296 write(4, "\211\1\0\0\3select count(*) as count fr"..., 397) = 397
16:54:09.895264 read(4, "\1\0\0\1\1\33\0\0\2\3def\0\0\0\5count\0\f?\0\25\0\0\0\10\201"..., 16384) = 544
16:54:09.896090 write(4, "\1\0\0\0\1", 5) = 5
16:54:09.896192 shutdown(4, 2 /* send and receive */) = 0

查询和来自 MySQLd 进程的响应之间大约有 1 毫秒(这是因为这是在本地主机上,并且结果在它的查询缓存中,顺便说一句)......并且 0.8 毫秒后执行了数据库关闭。那是在我用了 4 年的旧笔记本电脑上。

于 2012-08-21T15:32:32.387 回答
-1

更好的方法是只使用一个查询,因为只有一个与数据库的连接,而不是,如果您使用多个查询,那么与数据库的连接也很多,这个过程涉及:连接和断开连接,这样比较慢。

于 2012-08-21T01:18:49.243 回答
-2

关于 TerryE 的示例和使用 multi_query(!) 的建议,我检查了手册并更改了脚本以满足我的需要。最后我得到了一个如下所示的解决方案:

$sql  = 'SELECT COUNT(cat1_id) as `cat1` FROM `cat1`;';
$sql .= 'SELECT COUNT(cat2_id) as `cat2` FROM `cat2`;';
$sql .= 'SELECT COUNT(cat2_id) as `cat2_b` FROM `cat2` WHERE `date` >= DATE(NOW());';
$sql .= 'SELECT COUNT(cat3_id) as `cat3` FROM `cat3`;';
$sql .= 'SELECT COUNT(cat4_id) as `cat4` FROM `cat4`;';
$sql .= 'SELECT COUNT(cat5_id) as `cat5` FROM `cat5`;';
$sql .= 'SELECT COUNT(cat6_id) as `cat6` FROM `cat6`;';
$sql .= 'SELECT COUNT(cat7_id) as `cat7` FROM `cat7`;';

if ($db->multi_query($sql))
{ 
    do
    {
        if ($stmt = $db->store_result())
        {
            while ($row = $stmt->fetch_assoc())
            {
                foreach ($row as $key => $value)
                {
                    $count[$key] = $value;
                }
            }
            $stmt->free_result();
        }
    } while ($db->more_results() && $db->next_result());
}

与 TerryE 的示例存在一些差异,但结果是相同的。我知道开头有 7 行几乎相同,但只要我需要 WHERE 子句或其他内容,我更喜欢这个解决方案而不是需要手动添加查询或使用异常的 foreach 循环与if { ... }...

据我所知,我的解决方案应该没有问题,还是我错过了什么?

于 2012-08-22T04:53:56.180 回答