我无法获取需要生成的报告的SQL。我已经得到了(相当于)以下设置:
articles
(名称,manufacturer_id
等字段)。sales
。
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
我想我可以在晚上和晚上做同样的事,但文章会有所不同;例如,有些文章可能在早上但不在晚上销售。
同样,我能够构建一个查询,为我提供早上,晚上和晚上的销售,按类型分组。我想我的问题是我需要做两个GROUP BY才能得到这个报告。如果可能的话,我不知道该怎么做。
我正在使用PostgreSQL作为我的数据库,但我希望尽可能保持标准。如果它有帮助,使用它的应用程序是一个Rails应用程序。
答案 0 :(得分: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
表达式更改为CASE
expressions,这应该看起来像这样(未经测试):
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
答案 1 :(得分:2)
两个选项。
选项1.具有聚合计算列的单个连接
select article_name = a.article_name ,
morning_sales = sum( case when sales.type = 'morning' then sales.amount end ) ,
evening_sales = sum( case when sales.type = 'evening' then sales.amount end ) ,
night_sales = sum( case when sales.type = 'night' then sales.amount end ) ,
other_sales = sum( case when sales.type in ( 'morning','evening','night') then null else sales.amount end ) ,
total_sales = sum( sales.amount )
from articles a
join sales s on s.articles_id = a.articles_id
where sales.date between @dtFrom and @dtThru
group by a.article_name
选项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