在 INSERT > SELECT 中使用变量作为 Table 名称和 CONCAT_WS
Using a Variable as a Table Name with CONCAT_WS in INSERT > SELECT
我有一个数据库,用于存储各种仪器的读数。默认情况下,每个月都会在数据库中创建一个新的 table,并将该月的仪器读数插入 table。例如,2021 年 1 月的 table 称为 data_1_2021_01。
2021 年 2 月的 table 称为 data_1_2021_02。
以下存储过程创建一个 table 并从该月的 table(在示例中,data_1_2021_01)和第二个 table 插入特定月份的数据包含仪器标签信息。程序(如下所示)returns我所需要的结果符合预期:
DELIMITER //
CREATE
PROCEDURE db.DATAPREP ()
BEGIN
DROP TABLE IF EXISTS db.DATA;
CREATE TABLE db.DATA (
ts DATETIME,
tagid INT(11) NOT NULL,
tagpath VARCHAR(255),
curvalue FLOAT,
t_stamp BIGINT(20) NOT NULL,
INDEX (tagid , tagpath)
);
INSERT INTO db.DATA (
ts,
tagid,
tagpath,
curvalue,
t_stamp)
SELECT
FROM_UNIXTIME(data_1_2021_01.t_stamp/1000),
data_1_2021_01.tagid,
te.tagpath,
Case WHEN data_1_2021_01.FloatValue IS Null then data_1_2021_01.intValue
else data_1_2021_01.floatvalue END,
data_1_2021_01.t_stamp
FROM data_1_2021_01, te
where data_1_2021_01.tagid=te.id;
END //
DELIMITER ;
现在我需要自动化这个过程,这样存储过程就可以按月执行并从当月的 table 中提取数据,而无需手动更新 table 名称在程序中。我已声明变量以获取 table 名称,但无法使 INSERT INTO > SELECT 正常工作。如果我在 SELECT 语句中使用 CONCAT_WS 来标识 table/column 名称,它会尝试将结果存储为数据值,而不是将其视为要提取的 table/column来自的数据值。我试过使用 PREPARE、EXECUTE、DEALLOCATE,但我似乎也无法让它工作。我只是不太熟悉如何在 MySQL 中完成这项工作,因此非常感谢您的帮助。这是脚本(减去 PREPARE、EXECUTE、DEALLOCATE 语句):
DELIMITER //
CREATE
PROCEDURE db.DATAPREP ()
BEGIN
-- Set this month's table.
DECLARE CurrentMthTable varchar(16383);
-- Set the current year.
DECLARE CurrentYear char(4);
-- Set the current month.
DECLARE CurrentMth varchar(2);
-- GET the year for the current month table (%Y returns year as 4-digit value).
Set CurrentYear = DATE_FORMAT(curdate(), '%Y');
-- Get the current month (%m returns month as 2-digit value).
SET CurrentMth = DATE_FORMAT(curdate(), '%m');
-- The entire Table name
Set CurrentMthTable = CONCAT_WS("_", "data_1", CurrentYear, CurrentMth);
DROP TABLE IF EXISTS db.DATA;
CREATE TABLE db.DATA (
ts DATETIME,
tagid INT(11) NOT NULL,
tagpath VARCHAR(255),
curvalue FLOAT,
t_stamp BIGINT(20) NOT NULL
);
INSERT INTO db.DATA (
ts,
tagid,
tagpath,
curvalue,
t_stamp)
SELECT
FROM_UNIXTIME((CONCAT_WS(“”, CurrentMthTable, “.t_stamp”)/1000)),
CONCAT_WS("", CurrentMthTable, ".tagid"),
te.tagpath,
Case WHEN (CONCAT_WS("", CurrentMthTable, ".FloatValue")) IS Null then (CONCAT_WS("", CurrentMthTable, ".intValue")) else (CONCAT_WS("", CurrentMthTable, ".floatvalue")) END,
CONCAT_WS("", CurrentMthTable, ".t_stamp")
FROM CurrentMthTable, te
where (CONCAT_WS("", CurrentMthTable, ".tagid"))=te.id
));
END //
DELIMITER ;
就其价值而言,SQL 不能将替代变量用于模式项的名称,例如 tables、列、数据库、索引、存储过程等。它就是不能。这不仅仅是 MYSql 的限制,它适用于各种 SQL 数据库服务器。
正如您所发现的,您可以通过使用 MySQL "server side prepared statements." 来完成您需要的操作。这是一种使用字符串处理来创建 SQL 语句,然后执行它们的方法。
如果您在存储过程中执行此操作,则需要编写 SQL 字符串处理代码来创建 SQL 的文本。像这样把上个月的 table 复制到这个月的,然后擦除数据?
DECLARE stmt VARCHAR(16000);
SELECT CONCAT_WS("",
"CREATE TABLE data_1_",
DATE_FORMAT(NOW(), '%Y_%c'),
" AS SELECT * FROM data_1_",
DATE_FORMAT(NOW() - INTERVAL 1 MONTH, '%Y_%c'), ';')
INTO stmt;
/* now stmt is CREATE TABLE data_1_2021_2 AS SELECT * FROM data_1_2021_1; */
PREPARE s1 FROM stmt;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SELECT CONCAT_WS("",
"TRUNCATE TABLE data_1_",
DATE_FORMAT(NOW(), '%Y_%c'),';')
INTO stmt;
/* now stmt is TRUNCATE TABLE data_1_2021_2; */
PREPARE s1 FROM stmt;
EXECUTE s1;
DEALLOCATE PREPARE s1;
我的要点:即使是简单的东西,也很难正确编程。而且读起来也够难的,以后排错的时候推理起来就麻烦了。
而且,就其价值而言, 最好使用一个 table 来获取所有仪器的所有数据,而不是 table 每个月。严重地。从单个大型且正确索引的 table 访问记录会给数据库服务器带来大致相同的负载,编程更简单,并且可以从几个月到几年到几十年优雅地扩展。每个月 table 是不明智的。
我有一个数据库,用于存储各种仪器的读数。默认情况下,每个月都会在数据库中创建一个新的 table,并将该月的仪器读数插入 table。例如,2021 年 1 月的 table 称为 data_1_2021_01。 2021 年 2 月的 table 称为 data_1_2021_02。
以下存储过程创建一个 table 并从该月的 table(在示例中,data_1_2021_01)和第二个 table 插入特定月份的数据包含仪器标签信息。程序(如下所示)returns我所需要的结果符合预期:
DELIMITER //
CREATE
PROCEDURE db.DATAPREP ()
BEGIN
DROP TABLE IF EXISTS db.DATA;
CREATE TABLE db.DATA (
ts DATETIME,
tagid INT(11) NOT NULL,
tagpath VARCHAR(255),
curvalue FLOAT,
t_stamp BIGINT(20) NOT NULL,
INDEX (tagid , tagpath)
);
INSERT INTO db.DATA (
ts,
tagid,
tagpath,
curvalue,
t_stamp)
SELECT
FROM_UNIXTIME(data_1_2021_01.t_stamp/1000),
data_1_2021_01.tagid,
te.tagpath,
Case WHEN data_1_2021_01.FloatValue IS Null then data_1_2021_01.intValue
else data_1_2021_01.floatvalue END,
data_1_2021_01.t_stamp
FROM data_1_2021_01, te
where data_1_2021_01.tagid=te.id;
END //
DELIMITER ;
现在我需要自动化这个过程,这样存储过程就可以按月执行并从当月的 table 中提取数据,而无需手动更新 table 名称在程序中。我已声明变量以获取 table 名称,但无法使 INSERT INTO > SELECT 正常工作。如果我在 SELECT 语句中使用 CONCAT_WS 来标识 table/column 名称,它会尝试将结果存储为数据值,而不是将其视为要提取的 table/column来自的数据值。我试过使用 PREPARE、EXECUTE、DEALLOCATE,但我似乎也无法让它工作。我只是不太熟悉如何在 MySQL 中完成这项工作,因此非常感谢您的帮助。这是脚本(减去 PREPARE、EXECUTE、DEALLOCATE 语句):
DELIMITER //
CREATE
PROCEDURE db.DATAPREP ()
BEGIN
-- Set this month's table.
DECLARE CurrentMthTable varchar(16383);
-- Set the current year.
DECLARE CurrentYear char(4);
-- Set the current month.
DECLARE CurrentMth varchar(2);
-- GET the year for the current month table (%Y returns year as 4-digit value).
Set CurrentYear = DATE_FORMAT(curdate(), '%Y');
-- Get the current month (%m returns month as 2-digit value).
SET CurrentMth = DATE_FORMAT(curdate(), '%m');
-- The entire Table name
Set CurrentMthTable = CONCAT_WS("_", "data_1", CurrentYear, CurrentMth);
DROP TABLE IF EXISTS db.DATA;
CREATE TABLE db.DATA (
ts DATETIME,
tagid INT(11) NOT NULL,
tagpath VARCHAR(255),
curvalue FLOAT,
t_stamp BIGINT(20) NOT NULL
);
INSERT INTO db.DATA (
ts,
tagid,
tagpath,
curvalue,
t_stamp)
SELECT
FROM_UNIXTIME((CONCAT_WS(“”, CurrentMthTable, “.t_stamp”)/1000)),
CONCAT_WS("", CurrentMthTable, ".tagid"),
te.tagpath,
Case WHEN (CONCAT_WS("", CurrentMthTable, ".FloatValue")) IS Null then (CONCAT_WS("", CurrentMthTable, ".intValue")) else (CONCAT_WS("", CurrentMthTable, ".floatvalue")) END,
CONCAT_WS("", CurrentMthTable, ".t_stamp")
FROM CurrentMthTable, te
where (CONCAT_WS("", CurrentMthTable, ".tagid"))=te.id
));
END //
DELIMITER ;
就其价值而言,SQL 不能将替代变量用于模式项的名称,例如 tables、列、数据库、索引、存储过程等。它就是不能。这不仅仅是 MYSql 的限制,它适用于各种 SQL 数据库服务器。
正如您所发现的,您可以通过使用 MySQL "server side prepared statements." 来完成您需要的操作。这是一种使用字符串处理来创建 SQL 语句,然后执行它们的方法。
如果您在存储过程中执行此操作,则需要编写 SQL 字符串处理代码来创建 SQL 的文本。像这样把上个月的 table 复制到这个月的,然后擦除数据?
DECLARE stmt VARCHAR(16000);
SELECT CONCAT_WS("",
"CREATE TABLE data_1_",
DATE_FORMAT(NOW(), '%Y_%c'),
" AS SELECT * FROM data_1_",
DATE_FORMAT(NOW() - INTERVAL 1 MONTH, '%Y_%c'), ';')
INTO stmt;
/* now stmt is CREATE TABLE data_1_2021_2 AS SELECT * FROM data_1_2021_1; */
PREPARE s1 FROM stmt;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SELECT CONCAT_WS("",
"TRUNCATE TABLE data_1_",
DATE_FORMAT(NOW(), '%Y_%c'),';')
INTO stmt;
/* now stmt is TRUNCATE TABLE data_1_2021_2; */
PREPARE s1 FROM stmt;
EXECUTE s1;
DEALLOCATE PREPARE s1;
我的要点:即使是简单的东西,也很难正确编程。而且读起来也够难的,以后排错的时候推理起来就麻烦了。
而且,就其价值而言, 最好使用一个 table 来获取所有仪器的所有数据,而不是 table 每个月。严重地。从单个大型且正确索引的 table 访问记录会给数据库服务器带来大致相同的负载,编程更简单,并且可以从几个月到几年到几十年优雅地扩展。每个月 table 是不明智的。