查询多个表时如何准确使用聚合函数?

lcm*_*lcm 6 mysql sql

我比编写一个包含聚合函数的查询,查询多个表以及得到准确数字的结果并希望能得到一些帮助的时间更难.

SQL小提琴

类别表示例

该类别是我需要报告的内容:

|----|-----------|
| id | name      |
|----|-----------|
| 1  | furniture |
| 2  | music     |
| 3  | kitchen   |
| 4  | adventure |
|----|-----------|
Run Code Online (Sandbox Code Playgroud)

产品表示例

产品表示例:

|-----|----------------|-------------|
| id  | name           | category_id |
|-----|----------------|-------------|
| 101 | couch          | 1           |
| 102 | chair          | 1           |
| 103 | drum           | 2           |
| 104 | flute          | 2           |
| 105 | pot            | 3           |
| 106 | pan            | 3           |
| 107 | kitchen sink   | 3           |
| 108 | unicorn saddle | 4           |
| 109 | unicorn shoes  | 4           |
| 110 | horse shampoo  | 4           |
|-----|----------------|-------------|
Run Code Online (Sandbox Code Playgroud)

活动表示例

我们想要在活动表中找到的视图数据(按类别):

|----|------------|-------|
| id | product_id | views |
|----|------------|-------|
| 1  | 101        | 1000  |
| 2  | 102        | 2000  |
| 3  | 103        | 3000  |
| 4  | 104        | 4000  |
| 5  | 105        | 5000  |
| 6  | 106        | 6000  |
| 7  | 107        | 7000  |
| 8  | 108        | 8000  |
| 9  | 109        | 9000  |
| 10 | 110        | 10000 |
|----|------------|-------|
Run Code Online (Sandbox Code Playgroud)

销售表示例

我们要放置的销售表查询平均销售(再次按类别).请注意,vendor_id很重要,因为单个产品可以由多个供应商承载.我遗漏了供应商表,因为这个问题不需要(我们可以在后面的例子中使用供应商ID进行查询).

|----|------------|-----------|--------|
| id | product_id | vendor_id | amount |
|----|------------|-----------|--------|
| 1  | 101        | 1         | 1000   |
| 2  | 102        | 1         | 900    |
| 3  | 103        | 1         | 2000   |
| 4  | 105        | 1         | 3000   |
| 5  | 107        | 1         | 5000   |
| 6  | 101        | 2         | 600    |
| 7  | 103        | 2         | 7000   |
| 8  | 105        | 2         | 8000   |
| 9  | 107        | 2         | 1000   |
| 10 | 108        | 1         | 500    |
| 11 | 109        | 1         | 600    |
| 12 | 108        | 2         | 400    |
| 13 | 109        | 2         | 500    |
|----|------------|-----------|--------|
Run Code Online (Sandbox Code Playgroud)

期望的输出

以下是所需的输出:

**请注意,某些供应商不携带某些产品,因此,意味着没有平均销售额.换句话说,产品表中的某些产品的销售表中没有记录(例如,没有供应商)携带马洗发水).出于这个原因,我想确保我使用的任何平均值或总和实际上是准确的.具体在这里,如果**.

|-----------|----------------|-----------|---------------|-------------------------------|-------------------------|
| category  | count_products | sum_views | average_sales | sum_views_where_sales_=>_1000 | sum_views_sales_<_1000  |
|-----------|----------------|-----------|---------------|-------------------------------|-------------------------|
| adventure | 3              | 27000     | 500           | 0                             | 27000                   |
| furniture | 2              | 3000      | 833           | 0                             | 3000                    |
| kitchen   | 3              | 18000     | 3000          | 6000                          | 12000                   |
| music     | 2              | 7000      | 5000          | 7000                          | 0                       |
|-----------|----------------|-----------|---------------|-------------------------------|-------------------------|
Run Code Online (Sandbox Code Playgroud)

当前查询状态

首先要准确了解产品和观点:

SELECT cat.name AS category,
        count(distinct p.name) AS product,
        sum(a.views) AS views
    FROM
        category AS cat,
        product AS p,
        activity AS a
    WHERE
        cat.id=p.category_id
    AND
        p.id=a.product_id
    GROUP BY 
        category;
Run Code Online (Sandbox Code Playgroud)

旁注:我希望不必在上面的查询中使用distinct.这里的任何想法都会很棒.

准确的结果按类别显示视图:

|-----------|---------|-------|
| category  | product | views |
|-----------|---------|-------|
| Adventure | 3       | 27000 |
| Furniture | 2       | 3000  |
| Kitchen   | 3       | 18000 |
| Music     | 2       | 7000  |
|-----------|---------|-------|
Run Code Online (Sandbox Code Playgroud)

在我开始加入其他表之前,一切看起来都很好:

SELECT cat.name AS category,
        count(distinct p.name) AS product,
        sum(a.views) AS views,
        round(avg(s.amount)) AS sales_amount
    FROM
        category AS cat,
        product AS p,
        activity AS a,
        sales AS s
    WHERE
        cat.id=p.category_id
    AND
        p.id=a.product_id
    AND
        p.id=s.product_id
    AND 
        s.vendor_id=1
    GROUP BY 
        category;
Run Code Online (Sandbox Code Playgroud)

问题输出

|-----------|---------|-------|------------------|
| category  | product | views | avg_sales_amount |
|-----------|---------|-------|------------------|
| Adventure | 2       | 17000 | 550              |
| Furniture | 2       | 3000  | 950              |
| Kitchen   | 2       | 12000 | 4000             |
| Music     | 1       | 3000  | 2000             |
|-----------|---------|-------|------------------|
Run Code Online (Sandbox Code Playgroud)

您可以注意到,当我开始通过vendor_id查询以获得平均销售额时,我将从所需的输出中获得更多.具体而言,产品列不再产生正确数量的产品,因为并非所有供应商都携带所有相同的产品,使得s.vendor_id = 1过滤器变得困难.我必须使用它来按供应商过滤这些报告,同时仍然在视图字段上获得准确的总和.

我已经使用LEFT JOIN尝试了上述查询,但最终仍然得到了无差错的结果,并且不确定需要发生什么,可能是某种子查询?

O. *_*nes 3

您的报告要求非常复杂。您可能在开始这个项目时认为它比实际情况要简单得多。

在这种情况下,您将根据独立制表的度量(浏览量和销量)来报告摘要。

因此,您需要从不将两个详细测量表连接在一起的聚合子查询开始。这是一个这样的查询。它可以让您按类别查看视图。 http://sqlfiddle.com/#!9/02f4b6/31/0

                   SELECT c.id category_id, SUM(a.views) views
                     FROM activity a
                     JOIN product p ON a.product_id = p.id
                     JOIN category c ON p.category_id = c.id
                    GROUP BY c.id
Run Code Online (Sandbox Code Playgroud)

这是另一个此类查询。它可以让您按类别获得销售额。http://sqlfiddle.com/#!9/02f4b6/32/0

                   SELECT c.id category_id, 
                          SUM(s.amount) total_sales, 
                          AVG(s.amount) avg_sales
                     FROM sales s
                     JOIN product p ON s.product_id = p.id
                     JOIN category c ON p.category_id = c.id
                    GROUP BY c.id
Run Code Online (Sandbox Code Playgroud)

接下来您需要按类别计算产品数量。幸运的是,每种产品只能属于一个类别。 http://sqlfiddle.com/#!9/02f4b6/42/0

                   SELECT c.id category_id, 
                          COUNT(*) products 
                     FROM product p 
                     JOIN category c ON p.category_id = c.id
                    GROUP BY c.id
Run Code Online (Sandbox Code Playgroud)

现在,有必要将这些项目连接在一起。category从桌子和LEFT JOIN其他三个开始,像这样。http://sqlfiddle.com/#!9/02f4b6/51/0

SELECT c.name, aggproducts.products,
       aggviews.views, aggsales.avg_sales, 
       aggsales.total_sales
  FROM category c
  LEFT JOIN (
                   SELECT c.id category_id, SUM(a.views) views
                     FROM activity a
                     JOIN product p ON a.product_id = p.id
                     JOIN category c ON p.category_id = c.id
                    GROUP BY c.id
       ) aggviews ON c.id = aggviews.category_id
  LEFT JOIN (
                   SELECT c.id category_id, 
                          SUM(s.amount) total_sales, 
                          AVG(s.amount) avg_sales
                     FROM sales s
                     JOIN product p ON s.product_id = p.id
                     JOIN category c ON p.category_id = c.id
                    GROUP BY c.id
       ) aggsales ON c.id = aggsales.category_id
  LEFT JOIN (
                   SELECT c.id category_id, 
                          COUNT(*) products 
                     FROM product p 
                     JOIN category c ON p.category_id = c.id
                    GROUP BY c.id
       ) aggproducts ON c.id = aggproducts.category_id
Run Code Online (Sandbox Code Playgroud)

诀窍是为每个类别包含零行或一行的每个度量创建聚合子查询。如果任何聚合子查询每个类别包含多于一行,则由于 JOIN 组合爆炸,您会开始出现行重复。

然后将LEFT JOIN这些聚合子查询添加到类别表中。不要使用普通,JOIN因为如果任何聚合子查询缺少特定类别,这将抑制结果中的行。

请注意,您使用这些子查询就像它们是表一样。这种从子查询构建查询的能力使结构化查询语言变得结构化

这些是基础知识。现在您需要另一个聚合子查询来获取这些条件总和。我要把那个留给你。