Преобразуйте хранимую процедуру Oracle с помощью REF_CURSOR и упакуйте глобальную переменную в Postgresql или MySQL. - PullRequest
2 голосов
/ 29 января 2009

Этот пакет использует две уникальные функции Oracle, REF_CURSOR и глобальную переменную пакета. Я хотел бы перенести функциональность с Oracle на Postgresql или 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;

Можете ли вы дать эквивалентный код? для Postgresql? для MySQL?

Примечание. Код Oracle является поточно-ориентированным. Это ключевая особенность.

Ответы [ 4 ]

6 голосов
/ 05 февраля 2009

PostgreSQL 8,3

Проблема в PostgreSQL заключается в отсутствии глобальных (или пакетных) переменных, поэтому эту часть нужно решать с помощью временной таблицы, которая создается первой. В остальном все было довольно просто.

Если вы серьезно относитесь к переносу приложения на PostgreSQL или MySQL, я бы порекомендовал вам вообще не использовать глобальные переменные, поскольку они являются плохой практикой при кодировании (по крайней мере, по мне:)

Но в любом случае вот код:

Это должно существовать до запуска функций:

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

Функции помещаются в схему для имитации пакета.

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;

Чтобы проверить, просто запустите это после того, как все было создано.

select * from tox.test('Test');
0 голосов
/ 03 февраля 2009

Вот решение, протестированное с MySQL 5.1.30.

Что касается требований безопасности потоков, механизм MySQL User Variable должен помочь. Это позволяет вам SET переменную, состояние которой ограничено текущим сеансом. Другие сеансы также могут создавать переменную с тем же именем и сохранять в ней другое значение.

Я предполагаю, что под безопасностью потока вы подразумеваете что-то вроде этого - состояние области сеанса. Потому что вы не можете иметь более детализированное поточно-безопасное состояние в базе данных. Каждый поток вашего приложения должен иметь свой собственный сеанс к базе данных.

В MySQL нет пакетов, поэтому пользовательская переменная является глобальной для сеанса. Другая хранимая процедура, которая использует переменную с тем же именем, будет конфликтовать.

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();
0 голосов
/ 04 февраля 2009

Мне трудно понять несколько вещей в вашем коде. Похоже, у вас есть таблица с двумя последовательностями, но только одна из них действительно является столбцом auto_increment.

В mysql auto_increment допускается только для одного столбца в таблице. Вы рассматривали вопрос о том, чтобы сделать другой столбец внешним ключом для автоматически увеличиваемого столбца другой таблицы?

Глобальная переменная хитрая, потому что у mysql их нет. Я думаю, что единственное решение - сохранить его в виде скаляра в таблице, а затем привязать к нему свои данные с помощью внешнего ключа.

Наконец, вернуть курсор ref легко, как я указывал в моем предыдущем ответе. В ссылке укажите (на другой ответ) вы можете увидеть пример кода.

0 голосов
/ 01 февраля 2009

Для MySQL:

  1. Для ref_cursor вы можете просто использовать обычный выбор в процедуре. Mysql имеет неявный набор результатов, который возвращается из хранимой процедуры, если вы выполняете оператор выбора. Смотрите мой ответ здесь .
  2. Для глобальной переменной пакета вы можете поместить ее в таблицу, но из вашего кода видно, что это последовательность, поэтому ее можно заменить полем auto_increment. Это должно быть довольно просто.

Было бы полезно, если бы вы могли опубликовать определение своей таблицы спула в вопросе. Тогда я, возможно, смогу предоставить вам точный код для mysql.

...