2011-05-09 205 views
2

我无法获取需要生成的报告的SQL。我有以下设置(的等价物):执行两个GROUP BY的SQL查询?

  • 一个表articles(字段,如姓名,manufacturer_id等)。
  • 表格sales
    • FK to文章叫article_id
    • 一个整数称为amount
    • 一个date
    • 的字段名为type。我们可以假定它是一个字符串,它可以有3个已知值 - 'morning''evening''night'

我想产生聚合的销售报告,给定一个开始日期和结束日期:

+--------------+---------------+--------------+-------------+ 
| article_name | morning_sales | evening_sales| night_sales | 
+--------------+---------------+--------------+-------------+ 
| article 1 |    0 |   12 |   2 | 
| article 2 |   80 |   3 |   0 | 
...   ...    ...   ...   ... 
| article n |   37 |   12 |   1 | 
+--------------+---------------+--------------+-------------+ 

我想尽可能有效地做到这一点。到目前为止,我已经能够生成一个查询,它会给我一种类型的销售(早上,晚上或晚上),但我无法同时为多种类型进行销售。它甚至有可能吗?

这是我到目前为止;它会给我的商品名,上午销量在一定时期内的所有文章的 - 换句话说,该报告的前两列:

SELECT articles.name as article_name, 
     SUM(sales.amount) as morning_sales, 
FROM "sales" 
INNER JOIN articles ON articles.id = sales.articles_id 
WHERE (sales.date >= '2011-05-09' 
    AND sales.end_date <= '2011-05-16' 
    AND sales.type = 'morning' 
) 
GROUP BY sales.article_id 

我想我可以为傍晚和晚上做同样的,但文章会有所不同;例如,一些文章可能在早上销售,但不在晚上销售。

  • 如果我必须为每个销售类型提出1个请求,我该如何“混合搭配”我将获得的不同文章列表?
  • 有没有更好的办法做到这一点(也许有些子查询)?

同样,我可以建立一个查询,让我所有的早上,晚上和夜间销售,按类型分组。我想我的问题是我需要做两个GROUP BY才能得到这个报告。我不知道该怎么做,如果可能的话。

我使用PostgreSQL作为我的数据库,但我想尽可能保持标准。如果有帮助,使用这个应用程序是一个Rails应用程序。

回答

4

幸运的是,您不需要使用数据库格式执行多个查询。这应该为你工作:

SELECT 
    articles.name AS article_name 
    SUM(IF(sales.type = 'morning', sales.amount, 0.0)) AS morning_sales, 
    SUM(IF(sales.type = 'evening', sales.amount, 0.0)) AS evening_sales, 
    SUM(IF(sales.type = 'night', sales.amount, 0.0)) AS night_sales 
FROM sales 
    JOIN articles ON sales.article_id = articles.id 
WHERE 
    sales.date >= "2011-01-01 00:00:00" 
    AND sales.date < "2011-02-01 00:00:00" 
GROUP BY sales.article_id 

而且如果有其他类型,你就必须添加更多的列存在,或者简单地通过增加这SELECT子句总结其他类型:

SUM(
    IF(sales.type IS NULL OR sales.type NOT IN ('morning', 'evening', 'night'), 
    sales.amount, 0.0 
) 
) AS other_sales 

以上是与MySQL兼容的。在Postgres的使用它,我想你可以到IF表述更改为CASEexpressions,它应该像这样(未经):

SELECT 
    articles.name AS article_name, 
    SUM(CASE WHEN sales.type = 'morning' THEN sales.amount ELSE 0.0 END) AS morning_sales, 
    SUM(CASE WHEN sales.type = 'evening' THEN sales.amount ELSE 0.0 END) AS evening_sales, 
    SUM(CASE WHEN sales.type = 'night' THEN sales.amount ELSE 0.0 END) AS night_sales 
FROM sales 
    JOIN articles ON sales.article_id = articles.id 
WHERE 
    sales.date >= "2011-01-01 00:00:00" 
    AND sales.date < "2011-02-01 00:00:00" 
GROUP BY sales.article_id 
+0

谢谢:)我一定是错过了读书的问题 – 2011-05-09 22:34:29

+0

感谢一个时很多!这帮了我很大的忙! – kikito 2011-05-10 07:27:15

2

两个选项。

选项1.单与计算列加入了agreggation

​​

选项2.多个左联接

select article_name = a.article_name , 
     morning_sales = sum(morning.amount) , 
     evening_sales = sum(evening.amount) , 
     night_sales = sum(night.amount ) , 
     other_sales = sum(other.amount ) , 
     total_sales = sum(total.amount ) 
from  articles a 
left join sales morning on morning.articles_id = a.articles_id 
          and morning.type  = 'morning' 
          and morning.date between @dtFrom and @dtThru 
left join sales evening on evening.articles_id = a.articles_id 
          and evening.type  = 'evening' 
          and evening.date between @dtFrom and @dtThru 
left join sales night on night.articles_id = a.articles_id 
          and night.type   = 'evening' 
          and night.date between @dtFrom and @dtThru 
left join sales other on other.articles_id = a.articles_id 
          and ( other.type is null 
           OR other.type not in ('morning','evening','night') 
          ) 
          and other.date between @dtFrom and @dtThru 
left join sales total on total.articles_id = a.articles_id 
          and total.date between @dtFrom and @dtThru 
group by a.article_name 
+0

相当不错;我喜欢选项2背后的想法,作为自连接的替代方案。但选项2有3个问题。 (1)您只选择了一个值,文章名!您还需要从自联接中选择值。 (2)根据查询的使用方式,可能需要列名。因此,查询应该以 'Select a.article_name,Sum(morning.amount)MorningAmount,Sum(evening.amount)EveningAmount', (依此类推)开始。 (3)注意'sum(Total.amount)'总是等于'sum(a.Amount)',所以你有更多的自连接比你需要;完全离开总计。 – 2011-05-10 00:37:04

+0

+1表示可以选择子选择。 – 2011-05-10 00:38:55

+0

我的查询与OP的例子相符。但是,如果您需要使用除列分组或聚合函数以外的列来修饰聚合结果集,则复杂程度会提高。有些人只是将它们添加到分组列表中,但根据我的经验,这会导致......有趣的结果......当数据(像往常一样)不如应有的那么干净。 – 2011-05-10 00:51:40