在 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 是不明智的。