MySQL-在具有多列的唯一行上转换具有匹配数据的多行

时间:2018-10-02 22:09:07

标签: mysql pivot

假设我有下一张表,第一个表只存储设备及其序列号和其他数据(与问题无关),因此可以简化为:

CREATE TABLE `tbl_devices` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `SERIAL` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `unique_serial` (`SERIAL`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

这些设备以一定频率发送带宽消耗日志,并将它们存储在下表中:

CREATE TABLE `tbl_log_bandwidth` (
  `ID` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `DEV_ID` int(11) NOT NULL,
  `DIR_ID` smallint(6) UNSIGNED NOT NULL,
  `PROTOCOL_ID` smallint(6) UNSIGNED NOT NULL,  
  `KILOBYTES` int(11) UNSIGNED NOT NULL,
  `EVAL_TIME` int(11) UNSIGNED NOT NULL,
  `DATE` datetime NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `idx_devices` (`DEV_ID`),
  KEY `idx_direction_id` (`DIR_ID`),
  KEY `idx_protocol_id` (`PROTOCOL_ID`),
  KEY `idx_devices_date` (`DEV_ID`,`DATE`),
  CONSTRAINT `fk_tbl_devices`
    FOREIGN KEY (`DEV_ID`) REFERENCES `tbl_devices` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_tbl_dir_type`
    FOREIGN KEY (`DIR_ID`) REFERENCES `tbl_dir_type` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_tbl_protocol_type`
    FOREIGN KEY (`PROTOCOL_ID`) REFERENCES `tbl_protocol_type` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

其他表( tbl_dir_type和tbl_protocol_type )仅存储静态信息:

CREATE TABLE `tbl_dir_type` (
  `ID` smallint(6) UNSIGNED NOT NULL,
  `NAME` varchar(50) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `tbl_dir_type` VALUES
    (1,'Download'),
    (2,'Upload');

CREATE TABLE `tbl_protocol_type` (
  `ID` smallint(6) UNSIGNED NOT NULL,
  `NAME` varchar(50) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `tbl_protocol_type` VALUES
    (1,'HTTP'),
    (2,'HTTPS'),
    (3,'POP3'),
    (4,'SMTP'),
    (5,'FTP'),
    (6,'ADB'),
    (99,'OTHER');

目标:

我需要实现的目标是,获得在特定小时范围内评估的最近N天中按天和方向ID分开的总带宽消耗的报告。到目前为止,我已经进行了下一个查询(假设N = 5,并且小时范围是09:00至18:00):

SELECT
    dev.SERIAL,
    DATE(log.DATE),
    SUM(CASE WHEN log.DIR_ID = 1 THEN log.KILOBYTES ELSE 0 END) / 1024 AS DOWNLOAD_MB,
    SUM(CASE WHEN log.DIR_ID = 2 THEN log.KILOBYTES ELSE 0 END) / 1024 AS UPLOAD_MB
FROM
    tbl_devices AS dev
INNER JOIN
    tbl_log_bandwidth AS log ON log.DEV_ID = dev.ID
WHERE
    log.DATE >= DATE_ADD(DATE(now()), INTERVAL -4 DAY)
AND
    HOUR(log.DATE) BETWEEN 09 AND 18
GROUP BY
    log.DEV_ID, log.DATE

上一个查询给我每个设备5行(每天1行),如下一个示例:

SERIAL               DATE        DOWNLOAD_MB  UPLOAD_MB
9000321982F3C585084  2018-10-02  34.5898      3.0186
9000321982F3C585084  2018-10-01  35.5703      3.2041
9000321982F3C585084  2018-09-30  34.9980      3.1123
9000321982F3C585084  2018-09-29  34.8496      3.0977
9000321982F3C585084  2018-09-28  91.0010      6.2588

但是,我想走得更远,对于具有下一个结构的每台设备,只能获得一行:

SERIAL              DOWN_2018-10-02 UP_2018-10-02 DOWN_2018-10-01 UP_2018-10-01 DOWN_2018-09-30 UP_2018-09-30 ...
9000321982F3C585084 34.5898         3.0186        35.5703         3.2041        34.9980         3.1123        ...

使用PIVOT功能进行查询更新

在阅读了一些枢轴示例之后,我进入了下一个查询:

SELECT
    dev.SERIAL,
    SUM(CASE WHEN log.DIR_ID = 1 AND DATE(log.DATE) = DATE(NOW())
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS DOWN_TODAY,
    SUM(CASE WHEN log.DIR_ID = 2 AND DATE(log.DATE) = DATE(NOW())
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS UP_TODAY,
    SUM(CASE WHEN log.DIR_ID = 1 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -1 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS DOWN_TODAY_MINUS_1DAY,
    SUM(CASE WHEN log.DIR_ID = 2 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -1 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS UP_TODAY_MINUS_1DAY,
    SUM(CASE WHEN log.DIR_ID = 1 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -2 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS DOWN_TODAY_MINUS_2DAYS,
    SUM(CASE WHEN log.DIR_ID = 2 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -2 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS UP_TODAY_MINUS_2DAYS,
    SUM(CASE WHEN log.DIR_ID = 1 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -3 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS DOWN_TODAY_MINUS_3DAYS,
    SUM(CASE WHEN log.DIR_ID = 2 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -3 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS UP_TODAY_MINUS_3DAYS,
    SUM(CASE WHEN log.DIR_ID = 1 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -4 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS DOWN_TODAY_MINUS_4DAYS,
    SUM(CASE WHEN log.DIR_ID = 2 AND DATE(log.DATE) = DATE_ADD(DATE(NOW()), INTERVAL -4 DAY)
             THEN log.KILOBYTES ELSE 0 END) / 1024 AS UP_TODAY_MINUS_4DAYS
FROM
    tbl_devices AS dev
INNER JOIN
    tbl_log_bandwidth AS log ON log.DEV_ID = dev.ID
WHERE
    log.DATE >= DATE_ADD(DATE(now()), INTERVAL -4 DAY)
AND
    HOUR(log.DATE) BETWEEN 09 AND 18
GROUP BY
    log.DEV_ID, dev.SERIAL

但是现在,我还有下一个问题:

1)如何处理列的动态性,这取决于评估中使用的最近N天的数量。

2),有什么方法可以评估表达式并将其用作列名?如您所见,我的列具有“ DOWN_TODAY ”,“ DOWN_TODAY_MINUS_1DAY ”等名称。但是,我希望它们像下一个表达式的结果一样被命名: / p>

CONCAT("DOWN_", DATE(NOW()))
CONCAT("DOWN_", DATE_ADD(DATE(NOW()), INTERVAL -1 DAY))
...

0 个答案:

没有答案