如何从laravel迁移创建此过程

j1dl9f46  于 2021-06-20  发布在  Mysql
关注(0)|答案(1)|浏览(340)

我正在尝试创建此过程(来源:https://mariadb.com/resources/blog/automatic-partition-maintenance-mariadb)从maria数据库中的laravel迁移:

DELIMITER $$
CREATE PROCEDURE db1.create_new_partitions(p_schema varchar(64), p_table varchar(64), p_months_to_add int)
   LANGUAGE SQL
   NOT DETERMINISTIC
   SQL SECURITY INVOKER
BEGIN  
   DECLARE done INT DEFAULT FALSE;
   DECLARE current_partition_name varchar(64);
   DECLARE current_partition_ts int;

   -- We'll use this cursor later to check
   -- whether a particular already exists.
   -- @partition_name_to_add will be
   -- set later.
   DECLARE cur1 CURSOR FOR 
   SELECT partition_name 
   FROM information_schema.partitions 
   WHERE TABLE_SCHEMA = p_schema 
   AND TABLE_NAME = p_table 
   AND PARTITION_NAME != 'p_first'
   AND PARTITION_NAME != 'p_future'
   AND PARTITION_NAME = @partition_name_to_add;

   -- We'll also use this cursor later 
   -- to query our temporary table.
   DECLARE cur2 CURSOR FOR 
   SELECT partition_name, partition_range_ts 
   FROM partitions_to_add;

   DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

   DROP TEMPORARY TABLE IF EXISTS partitions_to_add;

   CREATE TEMPORARY TABLE partitions_to_add (
      partition_name varchar(64),
      partition_range_ts int
   );

   SET @partitions_added = FALSE;
   SET @months_ahead = 0;

   -- Let's go through a loop and add each month individually between
   -- the current month and the month p_months_to_add in the future.
   WHILE @months_ahead <= p_months_to_add DO
      -- We figure out what the correct month is by adding the
      -- number of months to the current date
      SET @date = CURDATE();
      SET @q = 'SELECT DATE_ADD(?, INTERVAL ? MONTH) INTO @month_to_add';
      PREPARE st FROM @q;
      EXECUTE st USING @date, @months_ahead;
      DEALLOCATE PREPARE st;
      SET @months_ahead = @months_ahead + 1;

      -- Then we format the month in the same format used
      -- in our partition names.
      SET @q = 'SELECT DATE_FORMAT(@month_to_add, ''%Y%m'') INTO @formatted_month_to_add';
      PREPARE st FROM @q;
      EXECUTE st;
      DEALLOCATE PREPARE st;

      -- And then we use the formatted date to build the name of the
      -- partition that we want to add. This partition name is
      -- assigned to @partition_name_to_add, which is used in
      -- the cursor declared at the start of the procedure.
      SET @q = 'SELECT CONCAT(''p'', @formatted_month_to_add) INTO @partition_name_to_add';
      PREPARE st FROM @q;
      EXECUTE st;
      DEALLOCATE PREPARE st;

      SET done = FALSE; 
      SET @first = TRUE;

      -- And then we loop through the results returned by the cursor,
      -- and if a row already exists for the current partition, 
      -- then we do not need to create the partition.
      OPEN cur1;

      read_loop: LOOP
         FETCH cur1 INTO current_partition_name;

         -- The cursor returned 0 rows, so we can create the partition.
         IF done AND @first THEN
            SELECT CONCAT('Creating partition: ', @partition_name_to_add);

            -- Now we need to get the end date of the new partition.
            -- Note that the date is for the non-inclusive end range,
            -- so we actually need the date of the first day of the *next* month.

            -- First, let's get a date variable for the first of the partition month
            SET @q = 'SELECT DATE_FORMAT(@month_to_add, ''%Y-%m-01 00:00:00'') INTO @month_to_add';
            PREPARE st FROM @q;
            EXECUTE st;
            DEALLOCATE PREPARE st; 

            -- Then, let's add 1 month
            SET @q = 'SELECT DATE_ADD(?, INTERVAL 1 MONTH) INTO @partition_end_date';
            PREPARE st FROM @q;
            EXECUTE st USING @month_to_add;
            DEALLOCATE PREPARE st;

            -- We need the date in UNIX timestamp format.  
            SELECT UNIX_TIMESTAMP(@partition_end_date) INTO @partition_end_ts;

            -- Now insert the information into our temporary table
            INSERT INTO partitions_to_add VALUES (@partition_name_to_add, @partition_end_ts);
            SET @partitions_added = TRUE;
         END IF;

         -- Since we had at least one row returned, we know the
         -- partition already exists.
         IF ! @first THEN
            LEAVE read_loop;
         END IF;

         SET @first = FALSE;
      END LOOP;

     CLOSE cur1;
   END WHILE;

   -- Let's actually add the partitions now.
   IF @partitions_added THEN
      -- First we need to build the actual ALTER TABLE query.
      SET @schema = p_schema;
      SET @table = p_table;
      SET @q = 'SELECT CONCAT(''ALTER TABLE '', @schema, ''.'', @table, '' REORGANIZE PARTITION p_future INTO ( '') INTO @query';
      PREPARE st FROM @q;
      EXECUTE st;
      DEALLOCATE PREPARE st;

      SET done = FALSE;
      SET @first = TRUE;

      OPEN cur2;

      read_loop: LOOP
         FETCH cur2 INTO current_partition_name, current_partition_ts;

        IF done THEN
            LEAVE read_loop;
         END IF;

         -- If it is not the first partition, 
         -- then we need to add a comma
         IF ! @first THEN
            SET @q = 'SELECT CONCAT(@query, '', '') INTO @query';
            PREPARE st FROM @q;
            EXECUTE st;
            DEALLOCATE PREPARE st;
         END IF;

         -- Add the current partition
         SET @partition_name =  current_partition_name;
         SET @partition_ts =  current_partition_ts;         
         SET @q = 'SELECT CONCAT(@query, ''PARTITION '', @partition_name, '' VALUES LESS THAN ('', @partition_ts, '')'') INTO @query';
         PREPARE st FROM @q;
         EXECUTE st;
         DEALLOCATE PREPARE st;

         SET @first = FALSE;
      END LOOP;

      CLOSE cur2;

      -- We also need to include the p_future partition
      SET @q = 'SELECT CONCAT(@query, '', PARTITION p_future VALUES LESS THAN (MAXVALUE))'') INTO @query';
      PREPARE st FROM @q;
      EXECUTE st;
      DEALLOCATE PREPARE st;

      -- And then we prepare and execute the ALTER TABLE query.
      PREPARE st FROM @query;
      EXECUTE st;
      DEALLOCATE PREPARE st;  
   END IF;

   DROP TEMPORARY TABLE partitions_to_add;
END$$
DELIMITER ;

我使用heredoc设置变量:

$createTableProcedure = <<<SQL
DELIMITER $$
    CREATE PROCEDURE ...
    END
$$
DELIMITER ;
SQL;
    DB::unprepared($createTableProcedure);

这段代码在console或我的例子中运行良好。然而,laravel返回一个mariadb语法错误。这个答案(https://stackoverflow.com/a/18393771/928666)类似的问题说明:
分隔符不是有效的sql语句。这只是一个mysql客户端命令。所以不要用它。
好吧,我去掉了分隔符的更改,但现在在第6行出现语法错误,这是begin之后的第一个declare语句: DECLARE done INT DEFAULT FALSE; 我还为此找到了一堆(https://stackoverflow.com/a/36158005/928666)其中规定:
在存储过程定义前后缺少分隔符定义
所以现在我高兴地以循环结束。不管我做什么,最后都会出现语法错误。
那么,如何从laravel迁移创建这个过程呢?

atmip9wb

atmip9wb1#

您可以像这样在laravel中创建一个存储过程,这将呈现 DELIMITER 过时的:

$procedure = "
    CREATE PROCEDURE `your_new_procedure`(procedure_param_1 TEXT, procedure_param_2 INT)
    BEGIN
         // Your SP here
    END
";

DB::unprepared("DROP procedure IF EXISTS your_new_procedure");
DB::unprepared($procedure);

并称之为:

DB:raw(
'call your_new_procedure(?, ?),
[
    $var2,
    $var2,
]
);

来源:laravel中的存储过程https://medium.com/@smayzes/stored-程序-in-laravel-60e7cb255fc9

相关问题