具有变量表名的MySQL存储过程并检索丢失的记录

时间:2016-12-02 09:10:03

标签: mysql sql loops stored-procedures prepared-statement

我想要实现的是一个存储过程,它返回表中所有缺失id的记录集。 例如:

--------------------
| games            |
--------------------
| id | game name   |
--------------------
|  1 | first game  |
|  2 | second game |
|  5 | fifth game  |
| 10 | tenth game  |
-------------------

这应该导致:

----------
| result |
----------
| 3      |
| 4      |
| 6      |
| 7      |
| 8      |
| 9      |
----------

请注意,有多个表可以处理,因此我希望只有一个需要维护的存储过程,因此应该动态添加表名。

我在存储过程中找到了关于预处理语句,参数和循环的几个教程,但是我没有让组合工作。

我想要应用的逻辑是:

  1. 获得表中的最高ID
  2. 遍历表并检查迭代器是否以id
  3. 的形式出现
  4. 如果没有,添加到结果集并在循环完成后返回结果集
  5. 这是我到目前为止所得到的:

    CREATE DEFINER=`root`@`localhost` PROCEDURE `get_missing_ids`(IN in_kind varchar(15), OUT out_result int)
    BEGIN
        DECLARE highest_id INT;
        DECLARE i INT;
        DECLARE check_id INT;
    
        SET @highestid =  CONCAT("(CALL get_highest_id('", in_kind, "',@id)");
        PREPARE stmt1 FROM @highestid;
        SET highest_id = (EXECUTE stmt1);
    
        WHILE i < highest_id DO
            SET @checkid = CONCAT("SELECT COUNT(*) FROM ", in_kind, " WHERE id = ", i, ")");
            PREPARE stmt2 FROM @checkid;
            SET check_id = (EXECUTE stmt2);
            IF check_id = 0 THEN
                SELECT i;
            END IF;
        END WHILE;    
    END
    

    谁能解释我如何让这个组合起作用?

2 个答案:

答案 0 :(得分:0)

实际上你根本不需要循环。您只需要将源表与包含序列号的临时(或固定)表连接起来并进行比较。

要使其成为获取该表的表名和ID的存储过程,请尝试此存储过程。它适用于我的。

delimiter $$

use `test`$$

drop procedure if exists `FindMissedID`$$

create definer=`root`@`localhost` procedure `FindMissedID`(tableName varchar(100), tableNameID varchar(100))
begin

-- Create temporary table containing number for comparing source table
drop table if exists ListOfNumber;
create temporary table ListOfNumber(
    number int, 
    primary key (number)
)
engine = innodb;

-- Generate some numbers. Make it fit your needs
insert into ListOfNumber
    select @row := @row + 1 as row from 
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) tens,
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) hundreds,
    (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) thousands,
    -- etc
    (select @row:=0) r;

-- Make a prepared statement
set @SQL = concat("select ListOfNumber.number as MissingID"
        , char(10), "from ", tableName
        , char(10), "right join ListOfNumber on ", tableName, ".", tableNameID, " = ListOfNumber.number"
        , char(10), "where ", tableName, ".", tableNameID, " is null"
        , char(10), "order by ListOfNumber.number"
            );

-- Execute statement                
prepare statement from @SQL;
execute statement;
deallocate prepare statement;               

end$$

delimiter ;

试一试。

答案 1 :(得分:0)

使用this中的一些提示,您可以使用单个查询来查找缺少的ID:

DELIMITER //
CREATE PROCEDURE `get_missing_ids`()
BEGIN

    SELECT (t1.id + 1) as missing_starts_at, 
           (SELECT MIN(t3.id) -1 
             FROM games t3 
             WHERE t3.id > t1.id
            ) as missing_ends_at
    FROM games t1
    WHERE NOT EXISTS (SELECT t2.id 
                       FROM games t2 
                       WHERE t2.id = t1.id + 1)
    HAVING missing_ends_at IS NOT NULL;
END//

这将返回范围为:

的行
call `get_missing_ids`();
+-------------------+-----------------+
| missing_starts_at | missing_ends_at |
+-------------------+-----------------+
|                 3 |               4 |
|                 6 |               9 |
+-------------------+-----------------+

现在您可以使用该值。