SQL考虑到年龄,按产品计算销售额

时间:2015-06-25 11:39:29

标签: sql postgresql

我希望按发布日期分组计算销售额,但也按销售时的产品年龄进行分组,如下所示:

        | 3 months | 6 months    
2015-01 | 28.1     | 37.1
2015-02 | 29.3     | 35.6

因此,对于2015 - 01年发布的产品,28.1是每种类型的平均销售产品数量,发布后3个月。在发布日期后6个月,显然有更多的产品销售,37.1。

以下SQL获取销售清单:

SELECT
  d.item               AS title,
  d.quantity,
  a.firstdate          AS release_date,
  i.date               AS invoice_date,
  i.date - a.firstdate AS age

FROM invoices i
  JOIN invoice_details d ON i.id = d.invoice_id

  JOIN (SELECT
          d.item,
          d.binding,
          min(i.date) AS firstdate
        FROM invoices i
          JOIN invoice_details d ON i.id = d.invoice_id
        GROUP BY d.item, d.binding) AS a ON a.item = d.item AND a.binding = d.binding

WHERE
  i.discount != 100 AND d.price > 0
  AND (d.binding != 'Hardback' OR d.binding != 'Ebooks')

ORDER BY title, invoice_date

结果看起来像是:

title | quantity | release date | invoice date | age
A     | 1        | 2013-11-14   | 2013-11-14   | 0
A     | 2        | 2013-11-14   | 2013-12-14   | 30
A     | 3        | 2013-11-14   | 2014-01-14   | 60
A     | 4        | 2013-11-14   | 2014-02-14   | 90
A     | 5        | 2013-11-14   | 2014-03-14   | 120
B     | 6        | 2013-11-14   | 2013-11-14   | 0
B     | 7        | 2013-11-14   | 2013-12-14   | 30
B     | 8        | 2013-11-14   | 2014-01-14   | 60
B     | 9        | 2013-11-14   | 2014-02-14   | 90
B     | 10       | 2013-11-14   | 2014-03-14   | 120

对于产品A,2013-11-14发布日期后3个月的总销售额为1 + 2 + 3 = 6。对于产品B,3个月后的总销售额为6 + 7 + 8 = 21。 2013-11季度,3个月后每个季度的平均销售额为(6 + 21)/2=13.5

经过6个月((1 + 2 + 3 + 4 + 5)+(6 + 7 + 8 + 9 + 10))/ 2 = 27.5

发布日期只是产品销售的第一个日期 - 这就是加入的子查询的用途。可能有更好的方法。

我试过这个来获得3,6,12和24个月的平均值:

SELECT
  to_char(a.release_date, 'YYYY-MM') AS release_date,

  avg(CASE WHEN i.date - a.release_date < 92
    THEN d.quantity END)             AS three_months,

  avg(CASE WHEN i.date - a.release_date < 183
    THEN d.quantity END)             AS six_months,

  avg(CASE WHEN i.date - a.release_date < 365
    THEN d.quantity END)             AS twelve_months,

  avg(CASE WHEN i.date - a.release_date < 730
    THEN d.quantity END)             AS twentyfour_months

FROM invoices i
  JOIN invoice_details d ON i.id = d.invoice_id

  JOIN (SELECT
          d.item,
          d.binding,
          min(i.date) AS release_date
        FROM invoices i
          JOIN invoice_details d ON i.id = d.invoice_id
        GROUP BY d.item, d.binding) AS a ON a.item = d.item AND a.binding = d.binding

WHERE
  i.discount != 100 AND d.price != 0
  AND (d.binding != 'Hardback' OR d.binding != 'Ebooks')


GROUP BY release_date
ORDER BY release_date desc

显然这是完全错误的,因为它没有按标题对结果进行分组。它给了我每个订单的平均项目,而不是每个标题的平均项目。

顺便说一下,我坚持使用Postgres 8.2。

1 个答案:

答案 0 :(得分:0)

如果我理解正确,这就是你想要的:

SELECT
  to_char(date, 'YYYY-MM') AS release_date,
  avg(CASE WHEN age <  92 THEN quantity ELSE 0 END) AS three_months,
  avg(CASE WHEN age < 183 THEN quantity ELSE 0 END) AS six_months,
  avg(CASE WHEN age < 365 THEN quantity ELSE 0 END) AS twelve_months,
  avg(CASE WHEN age < 730 THEN quantity ELSE 0 END) AS twentyfour_months
FROM (
  SELECT d.item, d.quantity, (i.date - first_release.date) AS age, fr.date
  FROM invoice_details d
  JOIN (
    SELECT d.item, min(i.date) AS date
    FROM invoice_details d
    JOIN invoices i ON i.id = d.invoice_id
    WHERE d.binding != 'Hardback' AND d.binding != 'Ebooks'
    GROUP BY d.item) AS fr USING (item)
  JOIN invoice i ON i.id = d.invoice_id
  WHERE i.discount != 100 AND d.price > 0) AS foo
GROUP BY release_date
ORDER BY release_date;

这显然是未经测试的,因为我甚至不记得上次触摸8.2安装时的情况。您的版本没有公用表表达式或横向连接,在后续版本中列出了两个关键功能,这些功能可以使这更加直观。

无论如何,诀窍是首先计算每本发票相对于每本书销售日期的发票日期的年龄,然后在不同的时间段内对其进行平均。当我移动它们并略微改变它们时仔细查看过滤器((d.binding != 'Hardback' OR d.binding != 'Ebooks')很可能不是你想要的)。