Это вопрос из 2 частей. В настоящее время я нахожусь в процессе преобразования некоторых функций оракула в postgres.
Я преобразовал приведенное ниже в postgres, но проблема в том, что он выводит только одну строку:
CREATE TYPE m_t_stts_ot AS (
id INTEGER,
name TEXT,
stts_nm TEXT,
stts_ds TEXT,
f_s_f CHARACTER(1),
t_ts TIMESTAMP WITHOUT TIME ZONE,
e_id INTEGER,
s_efctv_dt TIMESTAMP WITHOUT TIME ZONE,
m_s_id INTEGER,
m_d_o_fl CHARACTER(1)
);
CREATE OR REPLACE FUNCTION get_s_fn(IN in_cur integer[], IN in_ts TIMESTAMP WITHOUT TIME ZONE)
RETURNS SETOF m_t_stts_ot
AS
$BODY$
DECLARE
o_rec s_ot;
v_id s_id%TYPE;
rec record;
BEGIN
FOR rec IN
SELECT sr.*,
mtsv.id,
mtsv.name,
mtsv.status ,
mtsv.stts_ds ,
mtsv.ts,
coalesce(mtsv.ts,sr.crtd_dt),
mtsv.e_dt,
mtsv.ovrrd_fl
FROM RAW sr
LEFT JOIN (select * from get_task_stts_fn(ARRAY(SELECT distinct id
FROM prpty
WHERE value_nb in (select * from unnest(in_cur))
AND prpty_id in( 20017, 21021, 22017)), in_ts))mtsv
ON ( sr.event_id = mtsv.event_id AND
mtsv.f_stts_fl = 'Y' AND
mtsv.task_ts <= in_ts )
WHERE sr.id in(select * from unnest(in_cur))
AND sr.crtd_dt <= cast(in_ts as date)
AND ( mtsv.id is not null OR
sr.event_id is null
)
ORDER BY ( case
when mtsv.id is not null then
task_ts
else
sr.crtd_dt
end ) asc
LOOP
IF ((o_rec).STTS_CD = 'A')
THEN
IF (((o_rec).c_cd IN ('E', 'R')) AND (rec.chng_ts > (o_rec).M_DT::DATE - INTERVAL '13 month') AND ((o_rec).ROLL_FL = 'Y')) THEN
o_rec.M_DT := (o_rec).M_DT::DATE + INTERVAL '12 month';
IF (o_rec).c_cd = 'R' THEN
o_rec.C_END_DT := (o_rec).M_DT::DATE - INTERVAL '12 month';
END IF;
END IF;
IF (((o_rec).c_cd NOT IN ('E', 'R')) AND (rec.chng_ts > (o_rec).M_DT::DATE - INTERVAL '7 month') AND ((o_rec).ROLL_FL = 'Y')) THEN
o_rec.M_DT :=(o_rec).M_DT::DATE + INTERVAL '12 month';
END IF;
IF (((o_rec).c_cd = 'E') AND (rec.chng_ts > (o_rec).M_DT::DATE - INTERVAL '12 month') AND ((o_rec).ROLL_FL = 'N')) THEN
o_rec.c_cd := 'DBT';
END IF;
END IF;
IF ((rec.c_type_cd LIKE 'SUB%' AND rec.c_type_cd NOT LIKE '%-AMNDT%') OR (rec.event_id IS NULL AND rec.c_type_cd IS NULL)) THEN
o_rec.s_id := rec.s_id;
o_rec.c_nb := rec.c_nb;
o_rec.f_nm := rec.f_nm;
o_rec.f_c_nm := rec.f_c_nm;
o_rec.f_c_e_tx := rec.f_c_e_tx;
o_rec.f_c_t_t := rec.f_c_t_t;
o_rec.f_a_t := rec.f_a_t;
o_rec.f_c_nm := rec.f_c_nm;
o_rec.f_s_c := rec.f_s_c;
o_rec.f_z_t := rec.f_z_t;
o_rec.g_s_t := rec.g_s_t;
o_rec.p_a := rec.p_a;
o_rec.a_b_a := rec.a_b_a;
o_rec.e_dt := rec.e_dt;
o_rec.M_DT := rec.M_DT;
o_rec.t_nb := rec.t_nb;
o_rec.i_t_cd := rec.i_t_cd;
o_rec.i_r := rec.i_r;
o_rec.i_r_tx := rec.i_r_tx;
o_rec.i_f_tx := rec.i_f_tx;
o_rec.c_cd := rec.c_cd;
o_rec.s_t_cd := rec.s_t_cd;
o_rec.s_s_cd := rec.s_s_cd;
o_rec.ROLL_FL := rec.ROLL_FL;
o_rec.C_END_DT := rec.C_END_DT;
o_rec.f_m_s_l_fl := rec.f_m_s_l_fl;
o_rec.p_p_p_fl := rec.p_p_p_fl;
o_rec.a_m_o_e_fl := rec.a_m_o_e_fl;
o_rec.e_tx := rec.e_tx;
o_rec.e_l_fl := rec.e_l_fl;
o_rec.r_u_u_fl := rec.r_u_u_fl;
o_rec.r_p_fl := rec.r_p_fl;
o_rec.n_o_n := rec.n_o_n;
o_rec.n_a := rec.n_a;
o_rec.n_s_d := rec.n_s_d;
o_rec.n_f_t := rec.n_f_t;
o_rec.a_m_o_e_fl := rec.a_m_o_e_fl;
o_rec.e_tx := rec.e_tx;
o_rec.s_a_i_a_fl := rec.s_a_i_a_fl;
o_rec.l_r_t_a_m_fl := rec.l_r_t_a_m_fl;
o_rec.d_o_fl := rec.d_o_fl;
o_rec.o_c_nb := rec.o_c_nb;
o_rec.d_cmt_tx := rec.d_cmt_tx;
o_rec.f_cmt_tx := rec.f_cmt_tx;
IF rec.name LIKE 'N L A' THEN
IF rec.stts LIKE 'S T F A' THEN
o_rec.s_s_cd := 'Approved';
o_rec.e_dt := COALESCE(rec.mrdt_sbl_efctv_dt::date, rec.task_ts::date);
o_rec.M_DT := (o_rec).e_dt + ((o_rec).t_nb::NUMERIC || ' days')::INTERVAL;
o_rec.d_o_fl := rec.m_d_o_fl;
IF (o_rec).c_cd = 'R' THEN
o_rec.C_END_DT := (o_rec).M_DT::DATE - INTERVAL '12 month';
END IF;
ELSIF rec.stts LIKE 'D' THEN
o_rec.s_s_cd := 'D';
ELSIF rec.stts LIKE 'W' THEN
o_rec.s_s_cd := 'W';
ELSIF rec.stts LIKE 'S C' THEN
o_rec.s_s_cd := 'C';
ELSE
o_rec.s_s_cd := 'S';
END IF;
END IF;
ELSIF ((rec.event_id IS NOT NULL AND rec.c_type_cd = 'S-R-A' AND rec.lfcyc_stts_ds = 'A') OR (rec.event_id IS NULL AND rec.c_type_cd = 'S-R-A'))
THEN
IF (rec.M_DT IS NOT NULL) THEN
o_rec.M_DT := rec.M_DT;
o_rec.t_nb := extract(epoch from age((o_rec).M_DT, (o_rec).e_dt))/3600;
IF (rec.ROLL_FL IS NOT NULL) THEN
o_rec.ROLL_FL := rec.ROLL_FL;
END IF;
IF (o_rec).c_cd = 'R' THEN
o_rec.C_END_DT := (o_rec).M_DT::DATE - INTERVAL '12 month';
END IF;
END IF;
IF (rec.i_t_cd IS NOT NULL) THEN
o_rec.i_t_cd := rec.i_t_cd;
o_rec.i_r := rec.i_r;
o_rec.i_r_tx := rec.i_r_tx;
o_rec.i_f_tx := rec.i_f_tx;
END IF;
ELSIF rec.event_id IS NULL
THEN
NULL;
END IF;
IF (o_rec).s_id IS NOT NULL
THEN
IF ((o_rec).s_s_cd = 'A')
THEN
IF (((o_rec).c_cd IN ('E', 'R')) AND (LEAST(current_date, (o_rec).M_DT::date) > (o_rec).M_DT::DATE - INTERVAL '13 month') AND ((o_rec).ROLL_FL = 'Y')) THEN
o_rec.M_DT := (o_rec).M_DT::DATE + INTERVAL '12 month';
IF (o_rec).c_cd = 'R' THEN
o_rec.C_END_DT := (o_rec).M_DT::DATE - INTERVAL '12 month';
END IF;
END IF;
IF (((o_rec).c_cd NOT IN ('E', 'R')) AND (LEAST(current_date, (o_rec).M_DT::date) > (o_rec).M_DT::DATE - INTERVAL '7 month') AND ((o_rec).ROLL_FL = 'Y')) THEN
o_rec.M_DT := (o_rec).M_DT::DATE + INTERVAL '12 month';
END IF;
IF (((o_rec).c_cd = 'E') AND (LEAST(current_date, (o_rec).M_DT::date) > (o_rec).M_DT::DATE - INTERVAL '12 month') AND ((o_rec).ROLL_FL = 'N')) THEN
o_rec.c_cd := 'DBT';
END IF;
END IF;
END IF;
END LOOP;
RETURN next o_rec;
END;
$BODY$
LANGUAGE plpgsql;
Затем я вызываю функцию, используя:
select * from get_s_fn(ARRAY(select v_nb::integer from p where p_s_d_p_id in( 20017, 21021, 22017)), now()::timestamp);
Работает нормально, но возвращает только 1 строку. Я хочу, чтобы он мог возвращать несколько строк. Я где-то читал, что составные типы занимают только первую строку, а затем отбрасывают остальные, есть ли способ обойти это? Я ищу ответ, который не требует от меня слишком больших изменений, у меня есть множество этих функций, которые мне нужно преобразовать, и я хочу изменить как можно меньше.
В следующей части я пытаюсь использовать оператор выбора с несколькими строками в качестве параметра IN для типа или курсора. Вот функция, которую я преобразовал (я не проверял, действительно ли она выполняется, потому что она не принимает несколько строк):
create type get_a_fn_type_in as(
s integer,
efctv_dt timestamp without time zone,
p_a float,
i_f_t text,
m_e_dt timestamp without time zone
);
CREATE OR REPLACE FUNCTION get_a_fn(in_cur get_a_fn_in[], IN in_ts TIMESTAMP WITHOUT TIME ZONE)
RETURNS SETOF s_p_n
AS
$BODY$
DECLARE
o_rec s_p_o;
v_s_id integer;
v_i_f_t text;
v_efctv_dt timestamp without time zone;
v_p_a integer;
v_m_e_dt timestamp without time zone;
v_m_dt timestamp without time zone;
v_i_r S_R.i_r%TYPE;
v_i_t_cd text;
v_a_a integer;
v_a_d timestamp without time zone;
rec record;
BEGIN
LOOP
select * from unnest(in_cur) INTO v_s_id, v_efctv_dt, v_p_a, v_i_f_t, v_m_e_dt;
EXIT WHEN (NOT FOUND);
SELECT
MAX(p_dt)
INTO STRICT v_a_d
FROM s_p_r
WHERE s_id = v_s_id AND pymnt_type_cd = 'I';
v_a_d := (CASE
WHEN v_a_d IS NULL THEN v_efctv_dt
ELSE v_a_d
END)::TIMESTAMP WITHOUT TIME ZONE;
v_i_f_t := UPPER(SUBSTR(TRIM(v_i_f_t), 1, 1));
v_m_dt := NULL;
v_i_r := NULL;
v_a_a := NULL;
v_a_d := (CASE
WHEN v_i_f_t = 'M' THEN v_a_d + INTERVAL '1 month'
WHEN v_i_f_t = 'Q' THEN v_a_d + INTERVAL '3 months'
WHEN v_i_f_t = 'S' THEN v_a_d + INTERVAL '6 months'
WHEN v_i_f_t = 'A' THEN v_a_d + INTERVAL '12 months'
WHEN v_i_f_t = 'D' THEN v_a_d + INTERVAL '1 days'
ELSE NULL
END)::TIMESTAMP WITHOUT TIME ZONE;
BEGIN
SELECT s_fn.mtrty_dt,
s_fn.intrs_rt,
s_fn.intrs_type_cd
INTO v_m_dt,
v_i_r ,
v_i_t_cd
FROM
(select * from get_s_fn((v_s_id), ci_ts))s_fn;
BEGIN
END;
EXCEPTION
WHEN others THEN
NULL;
END;
IF ((v_a_d <= v_m_dt - INTERVAL '12 months') AND (v_m_e_dt IS NULL OR v_a_d < v_m_e_dt) AND (v_a_d <= in_ts) AND (v_p_a > 0) AND (v_i_t_cd = 'F'))
THEN
v_a_a := ((v_p_a * v_i_r) /
CASE
WHEN v_i_f_t = 'M' THEN 12
WHEN v_i_f_t = 'Q' THEN 4
WHEN v_i_f_t = 'S' THEN 2
WHEN v_i_f_t = 'A' THEN 1
WHEN v_i_f_t = 'D' THEN 365
ELSE NULL
END)::NUMERIC;
v_a_a := ROUND(v_a_a, 2);
ELSE
EXIT;
END IF;
IF (v_a_a > 0) THEN
RETURN NEXT ARRAY[ROW (v_s_id, 'I', v_a_d, v_a_a, 'Accrual', v_a_d)::s_p_ot];
END IF;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql;
Я звоню, используя
select * from get_a_fn(ARRAY[(select row(id::integer, efctv_dt::timestamp, p_a::float, i_f_tx::text, m_dt::timestamp) from s_raw)]::get_a_fn_type_in[], current_timestamp::timestamp)
И я получаю сообщение об ошибке: невозможно привести тип записи к get_a_fn_type_in. Так что проблема похожа на выше, за исключением того, что я хочу использовать несколько строк для in.
В обоих случаях я изучал использование курсоров, но я не слишком знаком с ними. Кроме того, для примеров функций, которые использовали курсоры, все они использовали курсор с оператором select внутри фактической функции, тогда как я хочу передать курсор в качестве параметра. Опять же, я хочу изменить как можно меньше, потому что есть много функций, которые я должен преобразовать, которые следуют подобному образцу. Я где-то читал о создании временной таблицы для хранения значений, но это не то, что я могу сделать, если это не может быть каким-то образом сделано внутри функции. Функция должна работать из простого выбора * из функции (a, b);