mysql - 在 INSERT > SELECT 中使用带有 CONCAT_WS 的变量作为表名
问题描述
我有一个存储各种仪器读数的数据库。默认情况下,每个月都会在数据库中创建一个新表,并将仪器读数插入该月的表中。例如,2021 年 1 月的表称为 data_1_2021_01。2021 年 2 月的表称为 data_1_2021_02。
以下存储过程创建一个表并从该月份的表(在示例中为 data_1_2021_01)和包含仪器标签信息的第二个表中插入特定月份的数据。该过程(如下所示)按预期返回我需要的结果:
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 ;
现在我需要自动化这个过程,这样存储过程可以按月执行,并从当月的表中提取数据,而无需手动更新过程中的表名。我已经声明了变量来获取表名,但我无法让 INSERT INTO > SELECT 正常工作。如果我使用 CONCAT_WS 来识别 SELECT 语句中的表/列名称,它会尝试将结果存储为数据值,而不是将其视为要从中提取数据值的表/列。我尝试过使用 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 不能使用替代变量来替代表、列、数据库、索引、存储过程等模式项的名称。它就是不能。这不仅仅是 MYSql 的限制,它适用于各种 SQL 数据库服务器。
正如您所发现的,您可以使用 MySQL “服务器端准备好的语句”来做您需要的事情。这是一种使用字符串处理来创建 SQL 语句,然后执行它们的方法。
如果在存储过程中执行此操作,则需要编写 SQL 字符串处理代码来创建 SQL 文本。像这样将上个月的表复制到本月的表中,然后擦除数据?
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;
我的要点:即使对于简单的东西,这也很难正确编程。而且很难读到您在将来进行故障排除时将难以推理。
而且,就其价值而言,您最好使用一个表格来存储来自所有仪器的所有数据,而不是每个月都使用一个表格。严重地。从单个大型且正确索引的表中访问记录会在数据库服务器上产生大致相同的负载,更易于编程,并且可以从几个月到几年甚至几十年优雅地扩展。每个月的一张桌子是不明智的。
推荐阅读
- html - 如何计算角度平均值?
- c# - xamarin 方法 GetOrCreateRenderer() 的替代方法
- azure-devops - 如何在 CI 或 msbuild 中使用不同的目标平台构建 SSDT 项目
- python - 从 django 后端返回所有数据
- rust - 我怎样才能避免给已经有明确生命周期的东西一个静态生命周期?
- c# - 与 Postman 相比,为什么 Angular 9 http 调用需要更长的时间?
- r - 尝试在 R 中安装 ggTimeSeries 包时,我不断收到“下标越界”错误 - 如何安装该包?
- xamarin.forms - Xamarin 表单 TabbedPage 图标很小
- python - 如何使用 _str_method 打印 myFirstCar 的字符串表示形式?
- phpmyadmin - 尝试查看表格时 phpmyadmin 错误 500