Conversion de la procédure stockée Oracle à l'aide de REF_CURSOR et de la variable globale du package en Postgresql ou MySQL

StackOverflow https://stackoverflow.com/questions/489441

Question

Ce package utilise deux fonctionnalités uniques d'Oracle, REF_CURSOR et une variable globale de package. Je souhaite transférer les fonctionnalités d'Oracle vers PostgreSQL ou MySQL.

PACKAGE tox IS
    /*=======================*/
        g_spool_key spool.key%TYPE := NULL;
        TYPE t_spool IS REF CURSOR RETURN spool%ROWTYPE;
    /*=======================*/
        PROCEDURE begin_spool;
    /*=======================*/
        PROCEDURE into_spool
            (
            in_txt IN spool.txt%TYPE
            );
    /*=======================*/
        PROCEDURE reset_spool;
    /*=======================*/
        FUNCTION end_spool
            RETURN t_spool;
    /*=======================*/
        FUNCTION timestamp
            RETURN VARCHAR2;
    /*=======================*/
    END tox;

PACKAGE BODY tox
    IS
    /*========================================================================*/
    PROCEDURE begin_spool
        AS
        /*=======================*/
        BEGIN
        /*=======================*/
            SELECT
                key.NEXTVAL
            INTO
                g_spool_key
            FROM
                DUAL;
        /*=======================*/
        END begin_spool;
    /*========================================================================*/
    PROCEDURE into_spool
        (
        in_txt IN spool.txt%TYPE
        )
        AS
        /*=======================*/
        BEGIN
        /*=======================*/
            INSERT INTO
                spool
            VALUES
                (
                g_spool_key,
                in_txt,
                seq.NEXTVAL
                );
        /*=======================*/
        END into_spool;
    /*========================================================================*/
    PROCEDURE reset_spool
        AS
        /*=======================*/
        BEGIN
        /*=======================*/
            DELETE
                spool
            WHERE
                key = g_spool_key;
            COMMIT;
            begin_spool;
        /*=======================*/
        END reset_spool;
    /*========================================================================*/
    FUNCTION end_spool
        RETURN t_spool
        AS
        v_spool t_spool;
        /*=======================*/
        BEGIN
        /*=======================*/
            COMMIT;
            OPEN v_spool FOR
                SELECT
                    *
                FROM
                    spool
                WHERE
                    key = g_spool_key
                ORDER BY
                    seq;
            RETURN v_spool;
        /*=======================*/
        END end_spool;
    /*========================================================================*/
    FUNCTION timestamp
        RETURN VARCHAR2
        AS
        /*-----------------------*/
        v_result VARCHAR2(14);
        /*=======================*/
        BEGIN
        /*=======================*/
            SELECT
                TO_CHAR(SYSDATE,'YYYYMMDDHH24MISS')
            INTO
                v_result
            FROM
                DUAL;
            RETURN v_result;
        /*=======================*/
        END timestamp;
    /*========================================================================*/
    END tox;

Pouvez-vous produire le code équivalent? pour Postgresql? pour MySQL?

Remarque: le code Oracle est thread-safe. C’est une caractéristique clé.

Était-ce utile?

La solution

PostgreSQL 8.3

Le problème dans PostgreSQL est le manque de variables globales (ou de paquet), de sorte que cette partie doit être résolue avec une table temporaire créée en premier. Le reste était assez facile.

Si vous souhaitez sérieusement porter l'application sur PostgreSQL ou MySQL, je vous recommanderais de ne pas utiliser de variables globales, car elles sont une mauvaise pratique lors du codage (du moins selon moi:))

Mais enfin, voici le code:

Ceci doit exister avant d'exécuter les fonctions:

create table spool (key integer, txt varchar(2048), seq integer);
create sequence s_key;
create sequence s_seq;
create schema tox;
create temp table globals (name varchar(10), value varchar(100), primary key(name));

Les fonctions sont mises dans le schéma tox pour simuler un package.

create or replace function tox.get_variable(var_name varchar) returns varchar as $$
declare 
    ret_val varchar(100);
begin
    select value into ret_val from globals where name = var_name;
    return ret_val;
end
$$ language plpgsql;

create or replace function tox.set_variable(var_name varchar, value anyelement) returns void as $$
begin
    delete from globals where name = var_name;
    insert into globals values(var_name, value);
end;
$$ language plpgsql;


create or replace function tox.begin_spool() returns integer as $$
begin
    perform tox.set_variable('key', nextval('s_key')::varchar);
    return tox.get_variable('key'); 
end;
$$ language plpgsql;

create or replace function tox.reset_spool() returns integer as $$
begin
    delete from spool where key = tox.get_variable('key')::integer;
    return tox.begin_spool();
end;
$$ language plpgsql;

create or replace function tox.into_spool(in_txt spool.txt%TYPE) returns void as $$
begin
    insert into spool values(tox.get_variable('key')::integer, in_txt, nextval('s_seq'));
end;
$$ language plpgsql;



create or replace function tox.end_spool(refcursor) returns refcursor as $$
declare
begin
    open $1 for select * from spool where key = tox.get_variable('key')::integer order by seq;
    return $1;
end;
$$ language plpgsql;



create or replace function tox.test(txt varchar(100)) returns setof spool as $$
declare 
    v_spool_key integer;
    cnt integer;
begin
    v_spool_key = tox.begin_spool();

    for cnt in 1..10 loop
    perform tox.into_spool(txt || cnt); 
    end loop;

    perform tox.end_spool('spool_cursor');
    return query fetch all from spool_cursor;
end;
$$ language plpgsql;

Pour tester, exécutez cette opération après que tout ait été créé.

select * from tox.test('Test');

Autres conseils

Pour mysql:

  1. Pour ref_cursor, vous pouvez simplement utiliser une sélection régulière dans une procédure. Mysql a un jeu de résultats implicite qui est renvoyé à partir d'une procédure stockée si vous émettez une instruction select. Voir ma réponse ici .
  2. Pour la variable globale du package, vous pouvez la placer dans une table, mais il ressort de votre code qu'il s'agit d'une séquence et qu'elle peut donc être remplacée par un champ auto_increment. Cela devrait être assez simple.

Cela vous aiderait si vous pouviez publier la définition de votre table de spool dans la question. Ensuite, je pourrais probablement vous fournir le code exact pour mysql.

J'ai du mal à comprendre plusieurs choses dans votre code. Il semble que vous ayez une table avec deux séquences, mais une seule d’entre elles est vraiment une colonne auto_increment.

Dans mysql, auto_increment n’est autorisé que sur une colonne de la table. avez-vous envisagé de transformer l'autre colonne en clé étrangère en une colonne incrémentée automatiquement d'une autre table?

La variable globale est délicate, car mysql ne les a pas. Je pense que la seule solution est de le stocker en tant que scalaire dans une table, puis d'y attacher vos données avec une clé étrangère.

Enfin, il est facile de renvoyer un curseur de référence, comme je l'ai indiqué dans ma réponse précédente. Dans le lien fournir (à une réponse différente), vous pouvez voir un exemple de code.

Voici une solution testée avec MySQL 5.1.30.

En ce qui concerne vos exigences en matière de sécurité des threads, utilisateur de MySQL Un mécanisme variable devrait aider. Cela vous permet de SET une variable dont l'état est limité à la session en cours. D'autres sessions peuvent également créer une variable du même nom et y conserver une valeur différente.

Je suppose que par thread-safety, vous entendez quelque chose comme ceci: état de session. Parce que vous ne pouvez pas vraiment avoir un état thread-safe plus précis dans une base de données. Chaque thread de votre application doit avoir sa propre session dans la base de données.

Il n'y a pas de paquet dans MySQL, donc la variable utilisateur est globale à la session. Une autre procédure stockée utilisant une variable du même nom sera en conflit.

CREATE TABLE spool (
  `key` INT,
  txt   VARCHAR(2048),
  seq   INT AUTO_INCREMENT PRIMARY KEY
);

CREATE TABLE spool_key (
  `key` INT AUTO_INCREMENT PRIMARY KEY
);

DELIMITER $$
CREATE PROCEDURE begin_spool ()
BEGIN
  DELETE FROM spool_key;
  INSERT INTO spool_key (`key`) VALUES (DEFAULT);
  SET @sp_key = LAST_INSERT_ID();
END $$

CREATE PROCEDURE into_spool(IN in_txt VARCHAR(2048))
BEGIN
  INSERT INTO spool (`key`, txt, seq) VALUES
    (@sp_key, in_txt, DEFAULT);
END $$

CREATE PROCEDURE reset_spool()
BEGIN
  DELETE spool FROM spool JOIN spool_key USING (`key`);
  CALL begin_spool();
END $$

CREATE PROCEDURE end_spool()
BEGIN
  SELECT *
  FROM spool JOIN spool_key USING (`key`)
  ORDER BY seq;
END $$
DELIMITER ;

CALL begin_spool();
CALL into_spool('now is the time');
CALL into_spool('for all good men');
CALL end_spool();
CALL reset_spool();
CALL into_spool('to come to the aid');
CALL into_spool('of their country');
CALL end_spool();

DROP FUNCTION IF EXISTS fmt_timestamp;
CREATE FUNCTION fmt_timestamp() RETURNS CHAR(14)
RETURN DATE_FORMAT(SYSDATE(), '%Y%m%d%H%i%s');

SELECT fmt_timestamp();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top