根据条件选择查询和计数

Posted

技术标签:

【中文标题】根据条件选择查询和计数【英文标题】:select query and count based on condition 【发布时间】:2012-09-07 06:05:16 【问题描述】:

我想选择所有类别,子类别并计算属于子类别的业务数量。这是我正在使用的 SQl 查询。

SELECT
    c.id, 
    c.name,
    c.slug,
    sc.id,
    sc.name,
    sc.slug,
    COUNT(bsc.id) AS business_count
FROM 
    fi_category c
LEFT JOIN 
    fi_subcategory sc ON c.id = sc.category_id AND (sc.deleted_at IS NULL) 
LEFT JOIN 
    fi_business_subcategory bsc ON sc.id = bsc.subcategory_id AND (bsc.deleted_at IS NULL) 
WHERE 
    (c.deleted_at IS NULL) 
GROUP BY 
    c.id, sc.id

但是我想做的还有更多,应该根据他们所属的城市过滤 business_count,即最后我想选择所有类别、子类别,但 business_count 应该有一个像 WHERE city.id = 1 这样的子句,我猜我必须使用计数作为我无法弄清楚的子查询。

下面是fi_business_subcategoryfi_city的关系结构。

1) fi_business_subcategory

+----+----------------+-------------+
| id | subcategory_id | business_id |
+----+----------------+-------------+

2) fi_business

+----+---------+-----------+
| id | name    | suburb_id |
+----+---------+-----------+

3) fi_suburb

+-----+--------+---------+
| id  | name   | city_id |
+-----+--------+---------+

4) fi_city

+----+--------+
| id | name   |
+----+--------+

我尝试了类似的方法,但这似乎不起作用

SELECT
    c.id, 
    c.name,
    c.slug,
    sc.id,
    sc.name,
    sc.slug,
    bsc.business_count
FROM 
    fi_category c
LEFT JOIN 
    fi_subcategory sc ON c.id = sc.category_id AND (sc.deleted_at IS NULL) 
LEFT JOIN (
    SELECT 
        COUNT(business_id) t1.business_count, t1.subcategory_id 
    FROM
        fi_business_subcategory t1
    LEFT JOIN
        fi_business t2 ON t2.id = t1.business_id
    LEFT JOIN
        fi_suburb t3 ON t3.id = t2.suburb_id
    LEFT JOIN
        fi_city t4 ON t4.id = t3.city_id
    WHERE
        t4.id = 1
    GROUP BY
        t1.subcategory_id
) bsc ON sc.id = bsc.subcategory_id AND (bsc.deleted_at IS NULL)
WHERE 
    (c.deleted_at IS NULL) 
GROUP BY 
    c.id, sc.id

我应该如何构建查询以实现我想要的?

【问题讨论】:

你能把你的表结构发到sqlfiddle.com @raheelshan 这是表格结构,对于迟到的回复,我深表歉意。 sqlfiddle.com/#!2/33275 对不起@Ibrahim Azhar Armar 但你能不能也提供一些数据 @raheelshan 当然,我很抱歉没有添加任何数据。现在就去做。 @raheelshan 添加了示例数据,这里是链接sqlfiddle.com/#!2/5adaa,我花了很长时间,因为我有大约一万条记录和额外的列,我必须编辑和更新。 【参考方案1】:

我认为您没有理由必须使用子查询。我相信您可以简单地将fi_businessfi_business_subcategory 组合成一个带括号的表格因子。

SELECT
    c.id, 
    c.name,
    c.slug,
    sc.id,
    sc.name,
    sc.slug,
    COUNT(bsc.id) AS business_count
FROM
    fi_category c
LEFT JOIN
    fi_subcategory sc ON c.id = sc.category_id AND (sc.deleted_at IS NULL)
LEFT JOIN (
        fi_business b
    INNER JOIN
        fi_business_subcategory bsc ON b.id = bsc.business_id AND (bsc.deleted_at IS NULL)
    INNER JOIN
        fi_suburb su ON su.id = b.suburb_id AND su.city_id = 1
    ) ON sc.id = bsc.subcategory_id
WHERE 
    (c.deleted_at IS NULL) 
GROUP BY 
    c.id, sc.id

我已经checked 认为这是对您的表结构有效的 SQL。我想它很有可能会产生预期的结果,即使你的小提琴还不包含任何数据。请参阅the manual on JOIN syntax,了解有关可以在连接中使用括号的位置的详细信息。

您也可能会问自己,您是否真的需要将所有连接都保留为左连接。使用内部连接编写东西会容易得多。

由于连接是从左到右执行的,您可以先执行内部连接,然后是一系列 连接。这避免了括号:

SELECT
    c.id cat_id,
    c.name cat_name,
    c.slug cat_slug,
    sc.id sub_id,
    sc.name sub_name,
    sc.slug sub_slug,
    COUNT(bsc.id) AS business_count
FROM
    fi_business b
INNER JOIN
    fi_business_subcategory bsc ON b.id = bsc.business_id
    AND (b.deleted_at IS NULL) AND (bsc.deleted_at IS NULL)
INNER JOIN
    fi_suburb su ON su.id = b.suburb_id AND su.city_id = 1
RIGHT JOIN
    fi_subcategory sc ON sc.id = bsc.subcategory_id
RIGHT JOIN
    fi_category c ON c.id = sc.category_id AND (sc.deleted_at IS NULL)
WHERE
    (c.deleted_at IS NULL)
GROUP BY
    c.id, sc.id

【讨论】:

谢谢 :),这是 fiddle 的链接。sqlfiddle.com/#!2/33275,我将测试此查询并回复您。 我现在正在用数据更新小提琴。但是您的旧查询不起作用,让我们测试一下新查询。 这个查询似乎有效。非常感谢,如果你想用样本数据进行测试,我已经用数据更新了小提琴。这是链接sqlfiddle.com/#!2/5adaa 这个查询给了我我想要的。不使用子选择或子查询还有其他出路吗? @j0k:因为我认为它们是两个答案。不同的想法是他们每个人的核心。任何一个都会完成工作。可能有理由更喜欢其中一个,而社区对不同替代方案的投票可能会被用来反映这些偏好。我阅读meta.stackexchange.com/q/25209的方式,这种做法是可以的,即使其他人可能选择不同。【参考方案2】:

如果您想使用子查询,正确的方法是用尽可能小的变化来表达您的第二个查询:

SELECT
    c.id, 
    c.name,
    c.slug,
    sc.id,
    sc.name,
    sc.slug,
    IFNULL(bsc.business_count, 0)
          -- turn NULL from left join into 0
FROM 
    fi_category c
LEFT JOIN 
    fi_subcategory sc ON c.id = sc.category_id AND (sc.deleted_at IS NULL) 
LEFT JOIN (
    SELECT 
        COUNT(*) business_count, t1.subcategory_id
          -- removed table name from alias name,
          -- and improved performance by simply counting rows
    FROM
        fi_business_subcategory t1
    LEFT JOIN
        fi_business t2 ON t2.id = t1.business_id
    LEFT JOIN
        fi_suburb t3 ON t3.id = t2.suburb_id
    LEFT JOIN
        fi_city t4 ON t4.id = t3.city_id
    WHERE
        t4.id = 1 AND (t1.deleted_at IS NULL)
          -- check deletion in subquery for performance
    GROUP BY
        t1.subcategory_id
) bsc ON sc.id = bsc.subcategory_id
          -- no longer need to check deletion here
WHERE 
    (c.deleted_at IS NULL) 
GROUP BY 
    c.id, sc.id

小提琴here.

【讨论】:

无法获取查询中发生的情况。我的网站上有几个城市,我需要根据城市显示带有业务数量的子类别列表。那么我在哪里告诉mysql只计算特定城市的记录呢? t4.id = 1 检查就在那里,就像您在原始查询中写的那样。当然,您可以完全放弃t4,而选择t3.city_id = 1 哦,好的。非常感谢您。有什么办法可以不使用子选择或子查询。 (实际上我正在使用 Doctrine Query Language)。非常感谢你的帮助,我真的很感激。【参考方案3】:

试试这个

select 
    c.id,
    c.name,
    count(sc.name) as Count
from fi_category as c
left join fi_subcategory as sc on sc.category_id = c.id
left join fi_business_subcategory as  fbs on fbs.subcategory_id = sc.id
inner join (
select 
    fb.name,
    fs.id,
    fs.city_id

from fi_business as fb 
inner join fi_suburb as fs on fs.id = fb.suburb_id
where fs.city_id = 1



) as  fb on fb.id = fbs.business_id
group by c.id

【讨论】:

它返回空集,而记录存在,如果甚至城市不存在,我希望mysql获取记录,唯一应该考虑的是在计算业务时。 您没有按子类别进行分组。并且您使用 inner join 将丢失前面 left joins 中的所有 NULL 值,这意味着您将丢失所有计数为零的行。 在查询内部和查询外部的两个位置在派生查询中使用左连接,以便它可以正常工作。如果你想要 0 而不是空值,请使用 ifnull(column name,0)

以上是关于根据条件选择查询和计数的主要内容,如果未能解决你的问题,请参考以下文章

根据来自其他数据帧的位置条件在数据帧上编写选择查询,scala

从同一个表mysql存储过程中选择具有不同条件的多个计数

如何按范围分组,或有条件地从查询结果中选择

缓慢的 MySQL 查询:有没有办法避免对左连接的每一行进行条件选择计数?

转置带有子选择和计数的 sql 查询

选择计数(*)和选择计数(字段名)之间的区别[重复]