使用 if 语句重用 count

Posted

技术标签:

【中文标题】使用 if 语句重用 count【英文标题】:reusing count with if statement 【发布时间】:2016-04-01 08:01:29 【问题描述】:

我想优化以下查询以避免多次计数。

SELECT product.Id,
    IF((SELECT COUNT(*) 
        FROM productorders 
        WHERE productorders.ProductId = product.Id) > 0,
       (SELECT COUNT(*) FROM productorders WHERE productorders.ProductId = product.Id),
       9999999999
    )
FROM product

有人可以提出相同的解决方案

我在幻数 9999999999 中使用了更复杂的逻辑,为了简单起见,我在这里避免使用它

此外,这个查询是一个更大的查询的一小部分......为了简单起见,我避免这样做

解释结果如下

+ 选项 id select_type table type possible_keys key key_len ref rows Extra

1 基本的 产品 指数 空值 类别 ID 8 空值 25 使用索引

3

依赖子查询 产品订单 参考 基本的 基本的 8 tabletest.product.Id 1 使用索引 2


依赖子查询 产品订单 参考 基本的 基本的 8 tabletest.product.Id 1 使用索引

【问题讨论】:

【参考方案1】:

使用 mysql 的优化解决方案用户定义变量

SET @counter := 0;
SELECT product.Id,
       @counter := (SELECT COUNT(*) FROM productorders WHERE productorders.ProductId = product.Id) as cond,
       IF(@counter > 0, @counter, 9999999999) as counter
FROM product

我发现您一直只想获取 product.Idcounter 字段 - 从子查询中选择:

SELECT product_id, counter(
       SELECT product.Id as product_id,
           @counter := (SELECT COUNT(*) FROM productorders WHERE productorders.ProductId = product.Id) as cond,
           IF(@counter > 0, @counter, 9999999999) as counter
       FROM product) as counter

【讨论】:

我收到 MySQL 的这个语法错误说:文档 #1064 - 你的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 2 行的“check, IF(@counter > 0, @counter, 9999999999) as counter FROM product”附近使用正确的语法 @appdevfan,是的,对不起,check 不是好别名,请将其更改为任何其他名称。它运作良好。查看我的编辑 可以避免'cond'行吗?我也只需要计数器行,由于某种原因我没有得到 EXPLAIN 的输出,我不知道如何解释 我的方法符合“避免多次计数运行”的要求。它运作良好。您可以使用自定义 counter 字段并忽略 cond 字段。你说过:“我只需要柜台行”,product.Id 怎么样? 是的,这符合我的要求,我只需要 productId 和 counter 列,但现在它正在输出我不需要的额外列“cond”,我不确定自定义计数器字段将如何帮助与那个【参考方案2】:

我认为在 IF 语句中使用它的查询之前将计数查询结果作为 MySQL 变量启动,或者在查询中将计数结果分配给内联变量并稍后重用变量可能是一个答案。可以在这里找到一些灵感:How to store Query Result in variable using mysql

【讨论】:

我在这里面临的挑战是将 IF 与 COUNT 分配给变量混合。 啊,现在明白问题所在了。您需要对每个产品 ID 进行计数。是的,我的回答无济于事。【参考方案3】:

用户最好用聚合查询,像这样

SELECT product.Id, count(productorders.ProductId) 
FROM product left outer join productorders 
on productorders.ProductId=product.Id 
group by product.Id

9999999999 的逻辑最好在客户端实现,使用幻数很危险。明确地实现它 - 如果订单数 ==0,做某事,否则 - 使用收到的数字。

如果您仍想使用幻数,请尝试以下查询:

SELECT product.Id, 
IF(count(productorders.ProductId)>0, 
count(productorders.ProductId), 9999999999)
FROM product 
left outer join productorders on productorders.ProductId=product.Id
group by product.Id

【讨论】:

这仍然有两个我想避免的计数 2 计数不会影响这种情况下的性能,请尝试在查询前使用说明。 我使用了解释,它给出了以下结果:id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY product index NULL CategoryId 8 NULL 25 Using index 3 DEPENDENT SUBQUERY productorders ref PRIMARY PRIMARY 8 tabletest.product .Id 1 使用索引 2 DEPENDENT SUBQUERY productorders ref PRIMARY PRIMARY 8 tabletest.product.Id 1 使用索引【参考方案4】:

编辑,这很好用,当没有行时返回 0:

SELECT product.Id,
       COUNT(productorders.ProductId)
FROM product
LEFT JOIN productorders ON productorders.ProductId = product.Id
GROUP BY productorders.Id

如果您希望它返回 99999999(我强烈建议您不要这样做)而不是零,您只需添加来自 sergey 的 if 语句并将其更改为:

SELECT product.Id,
       IF(COUNT(productorders.ProductId)>0, COUNT(productorders.ProductId), 99999999)
FROM product
LEFT JOIN productorders ON productorders.ProductId = product.Id
GROUP BY productorders.Id

在这种情况下,您的选择中有 2 个 COUNT 函数不应使查询变慢。

以下是我刚刚在自己的数据库中进行的类似查询的解释结果(可能是不好的示例,因为行数不多,我建议您在自己的数据库中自己尝试):

不带 IF 的解释:

EXPLAIN SELECT tbl_stam_land.Land_ID,
     COUNT(tbl_naw_adres.Land_ID)
FROM tbl_stam_land
LEFT JOIN tbl_naw_adres ON tbl_naw_adres.Land_ID = tbl_stam_land.Land_ID
GROUP BY tbl_naw_adres.Adres_ID;
/* Affected rows: 0  Gevonden rijen: 2  Waarschuwingen: 0  Duur van 1 query: 0,015 sec. */

用 IF 解释:

EXPLAIN SELECT tbl_stam_land.Land_ID,
     IF(COUNT(tbl_naw_adres.Land_ID) > 0, COUNT(tbl_naw_adres.Land_ID), 99999999)
FROM tbl_stam_land
LEFT JOIN tbl_naw_adres ON tbl_naw_adres.Land_ID = tbl_stam_land.Land_ID
GROUP BY tbl_naw_adres.Adres_ID;
/* Affected rows: 0  Gevonden rijen: 2  Waarschuwingen: 0  Duur van 1 query: 0,015 sec. */

0,015 秒纯粹来自传输延迟,两者都在 0,015 和 0,032 之间变化。

编辑:我的错,在我使用的程序中,它会自动添加查询运行的时间。查看这篇文章,了解如何衡量一个查询的执行时间:

mysql execution time

【讨论】:

我需要做的NULL检查不是productorders.ProductId而是productorders中的COUNT是否为零 我明白,但是左连接总是有一行,这就是为什么我认为这可能会解决这个问题。我的建议就像 sergey 说的那样只计算并检查它在 php 代码中是否为 0 您确定是否对同一件事进行两次计数不会对性能产生影响。我在 sql 方面不够专业,无法理解这一点 它不应该有一个重要的,聚合发生一次。它可能会产生非常小的影响,但就像我说的那样,您确定的唯一方法就是使用“解释”自己衡量它。

以上是关于使用 if 语句重用 count的主要内容,如果未能解决你的问题,请参考以下文章

在 Doctrine DBAL 中重用 QueryBuilder

重用语句和结果集是不是会释放其先前使用的资源?还是我必须在重用之前明确关闭它们?

重用 LINQ 查询

Oracle with重用子查询

如何在 Oracle SQL 语句中重用动态列?

在 SQL 语句中重用表达式