【问题标题】:calculate sales per day in MySQL在 MySQL 中计算每天的销售额
【发布时间】:2014-06-29 02:41:14
【问题描述】:

我有一个简单的表格,其中包含特定组的日期和总销售额:

date       | totalsales
=======================
2014-05-01 | 3000
2014-05-02 | 3100
2014-05-03 | 3500
2014-05-04 | 3650

我喜欢计算一些东西,比如:

  • 每天的销售额
  • 平均销售额
  • 增长百分比

结果应该看起来像(手动计算所以可能是错误的:))

date       | sales  | average | growth
=======================================
2014-05-01 |   0    |  0      |   0
2014-05-02 | 100    | 50      | 100
2014-05-03 | 400    | 166.66  | 400
2014-05-04 | 150    | 162.5   |  37.5

这甚至可以在 sql 语句中实现,还是我应该使用 PHP 或其他服务器软件计算?

【问题讨论】:

  • 一切都可以在mysql中完成
  • 您只需要 ORDER BY、SUM()、AVG() 和可能的 MAX()...
  • 看看预期的输出,没那么简单
  • user468891 的评论指的是 MySQL Group by 语句和聚合。正如 Anthony Raymond 所指出的,这不是一个简单的 group by。要获得这些计算,您需要参考数据集中的先前记录。那是迷失在一个正常的群里了。这可以通过使用 ROW_NUMBER 创建一个偏移数据集来完成。示例发布在下面。

标签: php mysql sql


【解决方案1】:

假设每个日期都有自己唯一的行,您可以通过像这样连接回原始表来做到这一点:

SELECT t1.Date, CASE WHEN t2.Date IS NULL THEN 0 ELSE (t1.totalsales - t2.totalsales)
END AS sales
FROM table t1
LEFT JOIN table t2 ON t2.Date = DATE_ADD(t1.Date, INTERVAL -1 DAY)
ORDER BY 1

这将至少为您提供第一列,并且您应该能够从那里计算出其余部分的数学。在此处使用带有 CASE 语句的左连接很重要,否则您将无法获得表中的最低日期(您的第一行)

如果每个日期没有有自己的唯一行,这种方法仍然可行,您只需要在日期列上使用 GROUP BY 和 SUM 在子查询中创建数据集。

【讨论】:

  • 上述方法并不总是有效。这假定先前的记录是针对前一天的。如果您有没有数据的周末,则第 1 天没有条目。即你加入NULL。您可以通过使用 ROW_NUMBER() 函数为每一行提供唯一引用来克服此限制。
【解决方案2】:

这是每行没有子选择的完整查询:(感谢 @nmarsh 编写了最难的部分)

参见 SQL 小提琴: http://sqlfiddle.com/#!2/be4654/34/0

SELECT 
  t1.Date,
  CASE
    WHEN t2.date IS NULL THEN 0 ELSE (t1.totalSales - t2.totalSales)
    END AS sales,
  CASE 
    WHEN t2.date IS NULL THEN 0 / (@curRow := @curRow + 1) ELSE ((@curSum := @curSum + (t1.totalSales - t2.totalSales)) / (@curRow := @curRow + 1))
    END AS average,
  CASE
    WHEN t3.date IS NULL AND t2.date IS NULL THEN 0
    WHEN t3.date IS NULL THEN (t1.totalSales - t2.totalSales)
    WHEN t2.date IS NULL THEN 0 ELSE ((t1.totalSales - t2.totalSales) * 100) / (t2.totalSales - t3.totalSales)
    END AS growth
FROM test t1
LEFT JOIN test t2 ON t2.date = DATE_ADD(t1.Date, INTERVAL -1 DAY)
LEFT JOIN test t3 ON t3.date = DATE_ADD(t2.Date, INTERVAL -1 DAY)
JOIN (SELECT @curRow := 0) r
JOIN (SELECT @curSum := 0) ct
ORDER BY 1;

原表:

date       | totalsales
=======================
2014-05-01 |   3000
2014-05-02 |   3100
2014-05-03 |   3500
2014-05-04 |   3650

输出

date       | sales  | average | growth
=======================================
2014-05-01 |   0    |  0      | 0
2014-05-02 | 100    | 50      | 100
2014-05-03 | 400    | 166.66  | 400
2014-05-04 | 150    | 162.5   | 37.5

【讨论】:

    【解决方案3】:

    您可以使用recursive statement。在每次迭代中,计算一天的请求数据,并删除第一天(最旧的)。

    您也可以使用 PHP 来执行此操作,这似乎更好,因为您不想在 MySQL 表上施加过多负载,以防它无法节省您的时间/计算。

    【讨论】:

    • 我发现当您可以使用基于数据库集的查询和良好的索引时,通常可以获得最佳性能。当您开始循环时,有时会遇到性能问题。特别是如果您必须在循环中进行数据库调用。如果你可以在内存中获取所有内容,那么循环你的表现可能还可以。
    【解决方案4】:

    不太确定数字,但如果您对结果更具体,我可以仔细检查。

    您可以使用 ROW_NUMBER() 创建 2 个数据集并将它们连接到 ROW_NUMBER() 和 ROW_NUMBER()-1 以获得当前值和以前值的偏移量以计算增长。示例:

    DECLARE @Data TABLE (SalesDate DATETIME, totalSales INT)
    
    INSERT INTO @Data (SalesDate , totalSales) VALUES ('2014-05-01' , 3000)
    INSERT INTO @Data (SalesDate , totalSales) VALUES ('2014-05-02' , 3100)
    INSERT INTO @Data (SalesDate , totalSales) VALUES ('2014-05-03' , 3500)
    INSERT INTO @Data (SalesDate , totalSales) VALUES ('2014-05-04' , 3650)
    
    
    SELECT  
          CurrentDt.SalesDate 
         ,ISNULL(CurrentDt.totalSales - PreviousDt.totalSales ,0)                       AS  Sales
         ,FirstDate.FirstDate
         , NULLIF(CAST((CurrentDt.SalesDate - FirstDate.FirstDate) AS INT)+1,0)         AS SellingDays
         ,(ISNULL(CurrentDt.totalSales - PreviousDt.totalSales ,0)) 
         / NULLIF(CAST((CurrentDt.SalesDate - FirstDate.FirstDate) AS INT)+1,0)         AS  AverageSales
    FROM
    
        (SELECT Min(SalesDate) AS FirstDate FROM @Data) AS FirstDate,
        /*Base Sales Data*/
        (
        SELECT
             ROW_NUMBER() OVER(ORDER BY SalesDate) AS RowNum
            ,SalesDate 
            ,totalSales
        FROM
            @Data 
        ) AS CurrentDt
    
        /*Previous Value for Growth*/
        LEFT JOIN 
        (
        SELECT 
             ROW_NUMBER() OVER(ORDER BY SalesDate) AS RowNum
            ,SalesDate 
            ,totalSales  
        FROM
            @Data 
        ) AS PreviousDt
            ON CurrentDt.RowNum  -1 = PreviousDt.RowNum 
    

    我用过 MSSQL,但是 MySQL 支持 ROW_NUMBER OVER。

    【讨论】:

      【解决方案5】:

      希望这个查询对你有帮助

      SELECT 
          sample.id,
          sample.date AS oggi,
          sample.value AS sales,
          ((SELECT SUM(sample.value) FROM sample WHERE  sample.date <= oggi    ) / (SELECT COUNT(sample.value) FROM sample WHERE  sample.date <= oggi    ) ) AS avarege,  
          sample.value / IF((SELECT sample.value FROM sample WHERE  sample.date =  (oggi - INTERVAL 1 DAY )) = 0,sample.value,(SELECT sample.value FROM sample WHERE  sample.date =  (oggi - INTERVAL 1 DAY ))) *100 AS 'growt-percent'
          -- (SELECT SUM(sample.value) FROM sample WHERE  sample.date <= oggi    ) AS somma,
          -- (SELECT count(sample.value) FROM sample WHERE  sample.date <= oggi    ) AS conta,
          -- (SELECT sample.value FROM sample WHERE  sample.date =  (oggi - INTERVAL 1 DAY )) as valoreieri,
      FROM sample 
      WHERE sample.date BETWEEN '2014-05-01 00:00:00' AND '2014-05-31 00:00:00'
      

      表格数据是

      id 日期值


      1  2014-05-01 00:00:00         0
      2  2014-05-02 00:00:00       100
      3  2014-05-03 00:00:00       400
      4  2014-05-04 00:00:00       150
      5  2014-05-05 00:00:00       200
      

      结果是

      id oggi 销售平均增长率


      1  2014-05-01 00:00:00       0  0.0000    (NULL)         
      2  2014-05-02 00:00:00     100  50.0000   100.0000       
      3  2014-05-03 00:00:00     400  166.6667  400.0000       
      4  2014-05-04 00:00:00     150  162.5000  37.5000        
      5  2014-05-05 00:00:00     200  170.0000  133.3333       
      

      请注意,我不仅使用日期时间字段 如果您对查询有疑问,请询问

      对不起我的英语不好

      编辑 最后 3 行被注释了,因为我只将它用于测试

      【讨论】:

        猜你喜欢
        • 2016-03-26
        • 1970-01-01
        • 2020-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多