Converti la procedura memorizzata Oracle utilizzando REF_CURSOR e la variabile globale del pacchetto in Postgresql o MySQL

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

Domanda

Questo pacchetto utilizza due funzionalità uniche di Oracle, REF_CURSOR e una variabile globale del pacchetto.Vorrei trasferire la funzionalità da Oracle a Postgresql o 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;

Puoi produrre il codice equivalente?per PostgreSQL?per MySQL?

Nota:Il codice Oracle è thread-safe.Questa è una caratteristica fondamentale.

È stato utile?

Soluzione

PostgreSQL 8.3

Il problema in PostgreSQL è la mancanza di variabili globali (o di pacchetto), quindi quella parte deve essere risolta con una tabella temporanea creata per prima. Il resto è stato abbastanza semplice.

Se sei seriamente intenzionato a trasferire l'applicazione su PostgreSQL o MySQL, ti consiglierei di non usare affatto le variabili globali poiché sono una cattiva pratica durante la codifica (almeno secondo me :))

Ma comunque, ecco il codice:

Questo deve esistere prima di eseguire le funzioni:

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));

Le funzioni vengono inserite nello schema tox per simulare un pacchetto.

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;

Per provare, esegui questo dopo aver creato tutto.

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

Altri suggerimenti

Per MySQL:

  1. Per ref_cursor puoi semplicemente utilizzare una selezione regolare in una procedura.Mysql ha un set di risultati implicito che viene restituito dalla procedura memorizzata se si emette un'istruzione select.Vedi la mia risposta Qui.
  2. Per la variabile globale del pacchetto, puoi inserirla in una tabella, ma dal codice risulta che si tratta di una sequenza, quindi può essere sostituita con un campo auto_increment.Dovrebbe essere piuttosto semplice.

Sarebbe utile se potessi pubblicare la definizione della tua tabella spool nella domanda.Quindi probabilmente potrei fornirti il ​​codice esatto per mysql.

Ho difficoltà a capire diverse cose nel tuo codice. Sembra che tu abbia una tabella con due sequenze, ma solo una di esse è veramente una colonna auto_increment.

In mysql auto_increment è consentito solo su una colonna in una tabella. hai considerato di trasformare l'altra colonna in una chiave esterna di una colonna auto incrementata di un'altra tabella?

La variabile globale è complicata, perché mysql non le ha. Penso che l'unica risoluzione sia quella di archiviarlo come uno scalare in una tabella e quindi legare i tuoi dati con una chiave esterna.

Infine, restituire un cursore ref è facile, come ho sottolineato nella mia risposta precedente. Nel link fornire (a una risposta diversa) puoi vedere un esempio di codice.

Ecco una soluzione testata con MySQL 5.1.30.

Per quanto riguarda i requisiti per la sicurezza dei thread, MySQL Utente Il meccanismo variabile dovrebbe aiutare. Ciò ti consente di SET una variabile la cui variabile lo stato è limitato alla sessione corrente. Altre sessioni possono anche creare una variabile con lo stesso nome e mantenerne un valore diverso.

Presumo per sicurezza del thread che intendi qualcosa del genere - stato con ambito sessione. Perché in un database non è possibile avere uno stato thread-grained più dettagliato. Ogni thread dell'applicazione deve avere una propria sessione nel database.

Non ci sono pacchetti in MySQL, quindi la variabile utente è globale per la sessione. Un'altra procedura memorizzata che utilizza una variabile con lo stesso nome è in conflitto.

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();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top