按月透视 MySQL 中的数据

Posted

技术标签:

【中文标题】按月透视 MySQL 中的数据【英文标题】:Pivot Data in MySQL by Month 【发布时间】:2016-06-30 14:36:46 【问题描述】:

我有一个包含这些字段(型号、日期、价格、数量)的表格 我需要一个 SQL(到 mysql),它给我一个带有型号名称、数量和总销售额(价格*数量)的动态表,但按月份(日期)分隔。例如。

            April           |         May          |     June
Model |quantity| Gross Sales|  quantity|Gross Sales| quantity | Gross Sales
  1       6         50           4        40           5            45
  2       10         30          3        9            5            15

谢谢!!

更新:

桌子:

    CREATE TABLE `sales` (
      `model` varchar DEFAULT NULL,
      `price` int DEFAULT NULL,
      `quantity` int(2) DEFAULT NULL,
      `date` timestamp
    )

【问题讨论】:

给我们你的表的实际定义,在这种情况下它真的会有所帮助。 更好的是,向我们展示您源表中的示例数据。 这涉及到我要说的复杂性:在应用程序级别执行此操作,而不是在查询中。 MySQL pivot row into dynamic number of columns的可能重复 我只需要使用 SQL 我无法在应用程序中执行此操作 【参考方案1】:

这很棘手,也许有更好的方法来做到这一点,但这是我的两分钱,注意在 MySQL 中分组列名是不可能的,因为只有列和列名之间的关系是 1:1。

下面是一些代码,它生成测试数据,然后构建一个依赖动态 SQL 的过程来生成返回所需结果所需的查询,该查询也是动态执行的。请注意,代码应该无错误地运行,但对 DROP 语句进行了注释,以防止对现有结构造成不必要的损坏。 RANDBETWEEN 过程在最终存储过程中不需要,但用于生成测试数据。

#DROP TABLE IF EXISTS sales;

CREATE TABLE `sales` (
      `model` varchar(64) DEFAULT NULL,
      `price` int DEFAULT NULL,
      `quantity` int(2) DEFAULT NULL,
      `date` datetime
    );

SET @VMinModel := 1;
SET @VMaxModel := 5;

SET @VMinPrice := 10;
SET @VMaxPrice := 100;

SET @VMinQuantity := 1;
SET @VMaxQuantity := 10;

SET @VMinTime := UNIX_TIMESTAMP('2016-01-01');
SET @VMaxTime := UNIX_TIMESTAMP('2016-12-31');

#DROP FUNCTION RANDBETWEEN;

DELIMITER $

CREATE FUNCTION RANDBETWEEN(VMin INTEGER UNSIGNED, VMax INTEGER UNSIGNED) RETURNS INTEGER UNSIGNED
DETERMINISTIC
BEGIN

    RETURN ROUND(RAND() * (VMax - VMin) + VMin);

END$

DELIMITER ;

INSERT INTO `sales` SELECT RANDBETWEEN(@VMinModel, @VMaxModel), RANDBETWEEN(@VMinPrice, @VMaxPrice), RANDBETWEEN(@VMinQuantity, @VMaxQuantity), FROM_UNIXTIME(RANDBETWEEN(@VMinTime, @VMaxTime)) FROM information_schema.COLUMNS LIMIT 1000;

SET @VXDimension := 'model';
SET @VYDimension := 'DATE_FORMAT(date, ''%Y-%m'')';
SET @VMeasures := 'quantity, quantity * price';
SET @VTable := 'sales';

#DROP PROCEDURE IF EXISTS singleDimensionMeltAndCast;

DELIMITER $

CREATE PROCEDURE singleDimensionMeltAndCast (VXDimension CHAR(255), VYDimension CHAR(255), VMeasures CHAR(255), VTable CHAR(255)) 
BEGIN

    SET SESSION group_concat_max_len = 65536;

    DROP TEMPORARY TABLE IF EXISTS YDimensionValues;
    SET @VCreateYDimensionValueTable := CONCAT('CREATE TEMPORARY TABLE YDimensionValues SELECT DISTINCT ', VYDimension, ' AS YDim1 FROM ', VTable, ' ORDER BY ', VYDimension, ' ASC;');

    PREPARE stmt FROM @VCreateYDimensionValueTable;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    DROP TEMPORARY TABLE IF EXISTS Measures;
    CREATE TEMPORARY TABLE Measures (measure CHAR(255));
    SET @VCreateMeasuresTable := CONCAT('INSERT INTO Measures VALUES(''', REPLACE(VMeasures, ', ', '''), ('''), ''')');

    PREPARE stmt FROM @VCreateMeasuresTable;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    SET @VYDimensionSQLFieldsComponent = (SELECT GROUP_CONCAT('SUM(IF(', VYDimension, ' = ''', YDim1, ''', ', measure, ', 0)) `[', YDim1, ' x ',  measure, ']`' SEPARATOR ', ') FROM YDimensionValues, Measures WHERE TRUE);
    SET @VYDimensionSQLGroupComponent = (SELECT GROUP_CONCAT('IF(', VYDimension, ' = ''', YDim1, ''', ', measure, ', 0)' SEPARATOR ', ') FROM YDimensionValues, Measures WHERE TRUE);

    SET @VFinalQuery := CONCAT('SELECT ', VXDimension, ', ', @VYDimensionSQLFieldsComponent, ' FROM ', VTable, ' GROUP BY ', @VYDimensionSQLGroupComponent, ' ORDER BY ', VXDimension, ';');
    PREPARE stmt FROM @VFinalQuery;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

END$

DELIMITER ;

CALL singleDimensionMeltAndCast(@VXDimension, @VYDimension, @VMeasures, @VTable);

如果您有任何问题,请告诉我。

问候,

詹姆斯

【讨论】:

以上是关于按月透视 MySQL 中的数据的主要内容,如果未能解决你的问题,请参考以下文章

数据透视图 - 3 个日期列,如何按月显示日期计数?

mysql 如何按月分组查询出当前年度每个月的短信数量(数据库中这个月要是为空的话就用0条怎么显示出来)

SQL按月统计,按日分组

Access SQL:按自定义案例排序,按月透视

按月计算的唯一客户数

带有计数的数据透视表 if