如何使 Mysql 变量在查询中起作用

How to make Mysql variables work in a query

我一直在努力尝试在我的 mysql/mariadb 数据库中生成自动聚合代码。我目前正在尝试使用变量的方法。我会提前承认我无论如何都不是数据库专家。我完全是自学成才,并且一直在努力为这个特定问题寻找足够的资源。我在下面包含了简化示例,哦,我正在使用 mariadb 10.1

这段代码应该可以在 mysql 5.6mariadb 10.0+ 中运行,我已经在 10.1 上测试过它并且可以运行。 这是我的 Table: 并且 SQL FIDDLE <- 由于某种原因不起作用。可能是动态列。我会留下它以防有人知道为什么。

CREATE TABLE data_points
(
  id          BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  device_id   INTEGER,
  dtime       DATETIME,
  sf          INTEGER(11), -- sample frequency or interval
  agg         INTEGER(11), -- aggregation type, actually a fk
  data_point BLOB,
  PRIMARY KEY (id),
  UNIQUE (device_id, dtime, sf, agg)
);

让我们插入一些数据:

INSERT INTO data_points
    (device_id, dtime, sf, agg, data_point)
VALUES
    (1, '2015-01-02 12:00:00', 1, 60, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 13:00:00', 1, 60, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 14:00:00', 1, 60, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 15:00:00', 1, 60, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 16:00:00', 1, 60, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45));

到目前为止,一切正常。我想要做的是在不同的时间段执行聚合,我的最低颗粒期是 60 秒。这是我遇到问题的地方,它可能很明显。

SELECT
  @dp_dtime := MAX(dtime),
  @dp_aa := MIN(ROUND(COLUMN_GET(data_point, 'aa' AS DOUBLE), 4)),
  @dp_ab := MIN(ROUND(COLUMN_GET(data_point, 'ab' AS DOUBLE), 4)),
  @dp_ac := MIN(ROUND(COLUMN_GET(data_point, 'ac' AS DOUBLE), 4))
FROM data_points
WHERE
  device_id = 1 AND
  dtime BETWEEN '2015/01/02 12:00:00' AND '2015/01/17 23:05:00' AND
  sf = 60 AND
  agg = 1;

INSERT INTO data_points
(device_id, dtime, sf, agg, data_point)
VALUES (8, @dp_dtime, 300, 2, COLUMN_CREATE('aa', @dp_aa, 'ab', @dp_ab, 'ac', @dp_ac));

这最终会创建另一行 NULL 语句中变量所在的所有位置。

select @dp_dtime, @dp_aa, @dp_ab, @pd_ac;
-- This results in NULL, NULL, NULL, NULL

此时我很确定我对变量做错了什么。 很晚了,一天 14 小时。我什至接近?有 better/easier 方法吗? 任何帮助将不胜感激。

编辑: 在我的实际用例中,列数取决于进行聚合的设备类型。列是 excel 样式 'aa' 到 'zz' 可能的。尽管我见过的最大宽度约为 150 列。这听起来像是一个糟糕的设计,但性能却令人惊讶,我分不清这些动态列和实际列之间的区别。 (至少只要你不需要索引它们)

可能是一个简单的错字:我看到了 @_dtime

UNIQUE索引中,把dtime放在最后;它将使查询更快。 迷你索引课程: 所有 = 列都应该在复合索引中排在第一位,顺序不限(基数几乎没有区别)。然后你可以放一个'range'(dtime)。范围之后的任何列都不用于过滤。参见 my cookbook

摆脱id并将UNIQUE索引提升为PRIMARY KEY;它将使查询更快。 迷你索引课程:辅助键(例如您的 UNIQUE)需要在键和数据之间跳转。 PRIMARY KEY 与数据聚类(在 InnoDB 中),从而避免了弹跳。相反,PK 上的 'range scan' 是 table.

上的范围

尝试以下查询。

SQL:

CREATE TABLE data_points
(
  id          BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  device_id   INTEGER,
  dtime       DATETIME,
  sf          INTEGER(11), -- sample frequency or interval
  agg         INTEGER(11), -- aggregation type, actually a fk
  data_point BLOB,
  UNIQUE (device_id, dtime, sf, agg)
);
INSERT INTO data_points
    (device_id, dtime, sf, agg, data_point)
VALUES
    (1, '2015-01-02 12:00:00', 1, 1, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 13:00:00', 1, 1, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 14:00:00', 1, 1, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 15:00:00', 1, 1, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45)),
    (1, '2015-01-02 16:00:00', 1, 1, COLUMN_CREATE('aa', 12, 'ab', 34, 'ac', 45));
select * from data_points;
SELECT
  @dp_dtime := MAX(dtime) as dp_dtime,
  @dp_aa := MIN(ROUND(COLUMN_GET(data_point, 'aa' AS DOUBLE), 4)) as dp_aa,
  @dp_ab := MIN(ROUND(COLUMN_GET(data_point, 'ab' AS DOUBLE), 4)) as dp_ab,
  @dp_ac := MIN(ROUND(COLUMN_GET(data_point, 'ac' AS DOUBLE), 4)) as dp_ac
FROM data_points
WHERE
  device_id = 1 AND
  dtime BETWEEN '2015/01/02 12:00:00' AND '2015/1/17 23:05:00' AND
  sf = 1 AND
  agg = 1;
INSERT INTO data_points
(device_id, dtime, sf, agg, data_point)
VALUES (8, @dp_dtime, 300, 2, COLUMN_CREATE('aa', @dp_aa, 'ab', @dp_ab, 'ac', @dp_ac));
select * from data_points;

输出:

mysql> select * from data_points;
+----+-----------+---------------------+------+------+----------------------------+
| id | device_id | dtime               | sf   | agg  | data_point                 |
+----+-----------+---------------------+------+------+----------------------------+
|  1 |         1 | 2015-01-02 12:00:00 |    1 |    1 |            aaabacDZ |
|  2 |         1 | 2015-01-02 13:00:00 |    1 |    1 |            aaabacDZ |
|  3 |         1 | 2015-01-02 14:00:00 |    1 |    1 |            aaabacDZ |
|  4 |         1 | 2015-01-02 15:00:00 |    1 |    1 |            aaabacDZ |
|  5 |         1 | 2015-01-02 16:00:00 |    1 |    1 |            aaabacDZ |
+----+-----------+---------------------+------+------+----------------------------+
5 rows in set (0.00 sec)

mysql> SELECT
    ->   @dp_dtime := MAX(dtime) as dp_dtime,
    ->   @dp_aa := MIN(ROUND(COLUMN_GET(data_point, 'aa' AS DOUBLE), 4)) as dp_aa,
    ->   @dp_ab := MIN(ROUND(COLUMN_GET(data_point, 'ab' AS DOUBLE), 4)) as dp_ab,
    ->   @dp_ac := MIN(ROUND(COLUMN_GET(data_point, 'ac' AS DOUBLE), 4)) as dp_ac
    -> FROM data_points
    -> WHERE
    ->   device_id = 1 AND
    ->   dtime BETWEEN '2015/01/02 12:00:00' AND '2015/1/17 23:05:00' AND
    ->   sf = 1 AND
    ->   agg = 1;
+---------------------+---------+---------+---------+
| dp_dtime            | dp_aa   | dp_ab   | dp_ac   |
+---------------------+---------+---------+---------+
| 2015-01-02 16:00:00 | 12.0000 | 34.0000 | 45.0000 |
+---------------------+---------+---------+---------+
1 row in set (0.00 sec)

mysql> INSERT INTO data_points
    -> (device_id, dtime, sf, agg, data_point)
    -> VALUES (8, @dp_dtime, 300, 2, COLUMN_CREATE('aa', @dp_aa, 'ab', @dp_ab, 'ac', @dp_ac));
Query OK, 1 row affected (0.00 sec)

mysql> select * from data_points;
+----+-----------+---------------------+------+------+-------------------------------------------------+
| id | device_id | dtime               | sf   | agg  | data_point                                      |
+----+-----------+---------------------+------+------+-------------------------------------------------+
|  1 |         1 | 2015-01-02 12:00:00 |    1 |    1 |            aaabacDZ                      |
|  2 |         1 | 2015-01-02 13:00:00 |    1 |    1 |            aaabacDZ                      |
|  3 |         1 | 2015-01-02 14:00:00 |    1 |    1 |            aaabacDZ                      |
|  4 |         1 | 2015-01-02 15:00:00 |    1 |    1 |            aaabacDZ                      |
|  5 |         1 | 2015-01-02 16:00:00 |    1 |    1 |            aaabacDZ                      |
|  6 |         8 | 2015-01-02 16:00:00 |  300 |    2 |       ▒  aaabac      (@      A@     ▒F@ |
+----+-----------+---------------------+------+------+-------------------------------------------------+
6 rows in set (0.00 sec)