返回

Firebird 的 FOR SELECT ... INTO ... DO 在 MySQL 中的实现方案

mysql

Firebird 的 FOR SELECT ... INTO ... DO 在 MySQL 中的替代方法

在将 Firebird 数据库迁移到 MySQL 时,存储过程的转换会带来一些挑战。特别是,当需要遍历结果集时,Firebird 中的 FOR SELECT 在 MySQL 中没有直接等价的功能。

问题:FOR SELECT 的用途

Firebird 的 FOR SELECT ... INTO ... DO 结构用于遍历结果集并执行基于每条记录的特定操作。它是一种强大的机制,可以简化复杂查询和数据操作。

解决方案:MySQL 中的替代方案

虽然 MySQL 中没有直接等价于 FOR SELECT,但有几种方法可以实现类似的功能:

  • 游标: 游标允许你逐行遍历结果集。然而,与 Firebird 中的 FOR SELECT 相比,游标的性能较低。

  • 存储过程: 存储过程可以存储和重复使用代码块,从而避免重复执行相同的查询,提高性能。

  • 用户定义函数 (UDF): UDF 允许你创建自定义函数并将其存储在数据库中,为复杂的重复操作提供更大的灵活性。

游标的使用

DECLARE bDone INT;
DECLARE personid INT; 
DECLARE personname VARCHAR(100); 
DECLARE companyname VARCHAR(50); 
DECLARE serviceID INT;
DECLARE servicetimestamp TIMESTAMP, 
DECLARE description VARCHAR(100)

DECLARE curs
CURSOR FOR select distinct a.personid, a.personname, a.serviceID, b.companyname, 
                from persons a 
                join companies b on (a.companyid = b.companyid)
               where b.companyId = companyid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET bDone = 1;

DROP TEMPORARY TABLE IF EXISTS tblTemp;
CREATE TEMPORARY TABLE IF NOT EXISTS tblTemp (
    personid INT, 
    personname VARCHAR(100), 
    companyname VARCHAR(50), 
    serviceID INTEGER,
    servicetimestamp TIMESTAMP, 
    description VARCHAR(100)
  );  

OPEN curs;

SET bDone = 0;

REPEAT FETCH curs INTO personid,
                       personname,
                       companyname,
                       serviceID;
                       
SELECT servicetimestamp, description 
FROM services
WHERE serviceId = serviceId
ORDER by servicetimestamp
LIMIT 1                       
INTO servicetimestamp, description;

INSERT INTO tblTemp VALUES(personid, personname, companyname, serviceID, servicetimestamp, description);

UNTIL bDone END REPEAT;

CLOSE curs;

SELECT * FROM tblTemp;

存储过程的使用

CREATE PROCEDURE get_services(
    companyID INTEGER
)
BEGIN
    DECLARE personid INT; 
    DECLARE personname VARCHAR(100); 
    DECLARE companyname VARCHAR(50); 
    DECLARE serviceID INT;
    DECLARE servicetimestamp TIMESTAMP, 
    DECLARE description VARCHAR(100)

    DECLARE curs
    CURSOR FOR select distinct a.personid, a.personname, a.serviceID, b.companyname, 
                    from persons a 
                    join companies b on (a.companyid = b.companyid)
                   where b.companyId = companyID;

    OPEN curs;

    FETCH curs INTO personid,
                       personname,
                       companyname,
                       serviceID;
    WHILE NOT (NOT curs IS OPEN)
    DO
        SELECT servicetimestamp, description 
        FROM services
        WHERE serviceId = serviceId
        ORDER by servicetimestamp
        LIMIT 1                       
        INTO servicetimestamp, description;

        INSERT INTO tblTemp VALUES(personid, personname, companyname, serviceID, servicetimestamp, description);

        FETCH curs INTO personid,
                       personname,
                       companyname,
                       serviceID;
    END WHILE;

    CLOSE curs;
END

UDF 的使用

CREATE FUNCTION get_services(
    companyID INTEGER
) RETURNS TABLE (
    personid INT, 
    personname VARCHAR(100), 
    companyname VARCHAR(50), 
    serviceID INT,
    servicetimestamp TIMESTAMP, 
    description VARCHAR(100)
)
BEGIN
    DECLARE curs
    CURSOR FOR select distinct a.personid, a.personname, a.serviceID, b.companyname, 
                    from persons a 
                    join companies b on (a.companyid = b.companyid)
                   where b.companyId = companyID;

    OPEN curs;

    FETCH curs INTO personid,
                       personname,
                       companyname,
                       serviceID;
    WHILE NOT (NOT curs IS OPEN)
    DO
        SELECT servicetimestamp, description 
        FROM services
        WHERE serviceId = serviceId
        ORDER by servicetimestamp
        LIMIT 1                       
        INTO servicetimestamp, description;

        INSERT INTO tblTemp VALUES(personid, personname, companyname, serviceID, servicetimestamp, description);

        FETCH curs INTO personid,
                       personname,
                       companyname,
                       serviceID;
    END WHILE;

    CLOSE curs;
    
    RETURN tblTemp;
END

最佳实践

  • 对于性能至关重要的情况,存储过程和 UDF 通常是实现 FOR SELECT 的最佳方法。
  • 根据具体情况,游标也可以是一种可行的选择,但性能可能会受到影响。

常见问题解答

  1. 为什么 Firebird 中的 FOR SELECT 在 MySQL 中没有直接等价物?
    Firebird 和 MySQL 采用不同的数据库引擎和查询语言。Firebird 的 FOR SELECT 使用基于游标的方法,而 MySQL 使用基于集的查询。

  2. 哪种替代方案最适合我?
    最合适的替代方案取决于性能要求、代码复杂性和所需的灵活性。存储过程和 UDF 通常最适合需要高性能和可重用的复杂查询的情况。

  3. 游标是否总是比存储过程慢?
    并非总是如此。在某些情况下,游标可能比存储过程更快。然而,对于复杂或涉及大量数据的查询,存储过程通常更有效。

  4. 是否可以使用游标来实现 UDF?
    是的,可以。然而,在大多数情况下,使用存储过程或直接在 UDF 中编写 SQL 代码会更有效。

  5. 如何确定最佳的替代方案?
    对应用程序进行基准测试并根据性能结果选择最佳替代方案。