Oracle sql to pgsql with table() function issue - sql

I am doing the migration from Oracle to pgsql and I've the the oracle sql like below:
select code,product_no,qty qty from t_ma_tb_inventory
where code is not null and status=2
and update_type in (select * from table(splitstr(:types,',')))
and tb_shop_id=:shopId
order by update_time
and the splitstr function like below:
CREATE OR REPLACE FUNCTION splitstr (p_string text, p_delimiter text) RETURNS SETOF STR_SPLIT AS $body$
DECLARE
v_length bigint := LENGTH(p_string);
v_start bigint := 1;
v_index bigint;
BEGIN
WHILE(v_start <= v_length)
LOOP
v_index := INSTR(p_string, p_delimiter, v_start);
IF v_index = 0
THEN
RETURN NEXT SUBSTR(p_string, v_start);
v_start := v_length + 1;
ELSE
RETURN NEXT SUBSTR(p_string, v_start, v_index - v_start);
v_start := v_index + 1;
END IF;
END LOOP;
RETURN;
END
$body$
LANGUAGE PLPGSQL
SECURITY DEFINER
;
-- REVOKE ALL ON FUNCTION splitstr (p_string text, p_delimiter text) FROM PUBLIC;
can someone help me to write the equivalent code in pgsql?Thank you very much

You don't need to write your own function - Postgres already has a built-in function for that: string_to_array
select code, product_no, qty qty
from t_ma_tb_inventory
where code is not null and status=2
and update_type = any ( string_to_array(:types,',') )
and tb_shop_id = :shopId
order by update_time;
If you are passing a text, but you need to compare that to an integer, you need to cast the resulting array:
select code, product_no, qty qty
from t_ma_tb_inventory
where code is not null and status=2
and update_type = any ( string_to_array(:types,',')::int[] )
and tb_shop_id = :shopId
order by update_time;
However: that is a pretty ugly workaround that is only necessary in Oracle because it has no support for real arrays in SQL (only in PL/SQL).
In Postgres it would be much better to pass an array of integers directly (e.g. make :types an int[]). Then no parsing or casting would be necessary:
select code, product_no, qty qty
from t_ma_tb_inventory
where code is not null and status=2
and update_type = any ( :types )
and tb_shop_id = :shopId
order by update_time;

Related

HOW TO DEAL WITH THESE ERROR ,PL/SQL Compilation unit analysis terminated & PSL-00201 : 'TABLE NAME' MUST BE DECLARED

I am creating a function in Oracle but it's not getting compiled and I am getting two errors
Error 1.
Error: PL/SQL: Compilation unit analysis terminated
Error 2.
Error(2,16): PLS-00201: identifier 'DIV_DUR_PRICE_TABLE' must be
declared
HERE IS THE CODE
CREATE OR REPLACE FUNCTION DIV_DAR(FID IN VARCHAR, DATE1 IN DATE, DATE2 IN DATE)
RETURN DIV_DUR_PRICE_TABLE PARALLEL_ENABLE AS
PRAGMA AUTONOMOUS_TRANSACTION;
CNT NUMBER;
V_RET DIV_DUR_PRICE_TABLE;
BEGIN;
EXECUTE IMMEDIATE 'DELETE from GTT_DIV_DUR_PRICE_TABLE';
Insert Into GTT_DIV_DUR_PRICE_TABLE
(select mydate, column1, column2, 0 as Final_value from tablename)
-- please refer my previous question to understand the code written ahead
declare
v_num integer := 1;
v_column1 number(8,2);
v_column2 number(8,2);
v_Final_value number(8,2);
begin
for rec in (select * from GTT_DIV_DUR_PRICE_TABLE order by mydate)
loop
if(v_num = 1) then
update tab set Final_value = column1 where mydate = rec.mydate;
else
if(rec.column2 is not null) then
update tab set Final_value =
v_Final_value * (v_column1/rec.column1) +
rec.column2 * (v_column1/v_Final_value) where mydate = rec.mydate;
else
update tab set Final_value =
v_Final_value * (rec.column1 / v_column1) where mydate = rec.mydate;
end if;
end if;
v_num:= v_num +1;
v_column1 := rec.column1;
v_column2 := rec.column2;
select final_value into v_Final_value from GTT_DIV_DUR_PRICE_TABLE
where mydate = rec.mydate;
end loop;
end;
SELECT
CAST(
MULTISET(
SELECT * FROM GTT_DIV_DUR_PRICE_TABLE order by P_DATE desc
)AS DIV_DUR_PRICE_TABLE
) INTO V_RET FROM DUAL;
COMMIT;
RETURN V_RET;
END DIV_DAR;

Drop table if it's empty / pass the result of count(*) to variable

I would like to check if a table is empty, and if it is, I would like drop it. I know this little function doesn't seem as a useful thing by itself, but I have a much longer function, so this is just the main part.
CREATE OR REPLACE FUNCTION public.cl_tbl(t_name character varying)
RETURNS void AS
$BODY$
DECLARE
rownum int;
BEGIN
SELECT COUNT(*) INTO rownum FROM format('myschema.%I',t_name);
IF rownum = 0 then
EXECUTE format('DROP TABLE myschema.%I',t_name);
END IF;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
My problem is, that the line
SELECT COUNT(*) INTO rownum FROM format('myschema.%I',t_name);
doesn't returns 0 if the table is empty, instead it returns 1 as the number of rows of the selection.
| count(bigint)
--------------------
1 | 0
I've tried this as well:
rownum := SELECT COUNT(*) FROM format('myschema.%I',t_name);
but the result is the same. How could I pass the real number of the rows of a given table?
You can use EXISTS() - SELECT EXISTS(SELECT * FROM table_name).
CREATE OR REPLACE FUNCTION public.cl_tbl(t_name character varying)
RETURNS void AS
$BODY$
DECLARE
x BOOLEAN;
BEGIN
EXECUTE format('select exists (select * from myschema.%I) t', t_name) INTO x;
IF x = False then
EXECUTE format('DROP TABLE myschema.%I',t_name);
END IF;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
Try using EXECUTE:
CREATE OR REPLACE FUNCTION public.cl_tbl(t_name character varying)
RETURNS void AS
$BODY$
DECLARE
rownum int;
BEGIN
EXECUTE format('select count(*) from %I', t_name) into rownum;
IF rownum = 0 then
EXECUTE format('DROP TABLE %I',t_name);
END IF;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
;

How to recursively return table in postgres function using plpgsql? Without using CTE/With Recursive

I tried implementing it here as follows
create or replace function getTextEditRecord(textId integer)
RETURNS Table (
text_id integer,
text_details character varying,
new_text_id integer) AS $$
DECLARE
curr_rec record;
temp_rec record;
BEGIN
curr_rec :=
(select tm.text_id, tm.text_details, tm.new_text_id from text_master tm
where tm.text_id = textId);
IF FOUND THEN
IF curr_rec.text_id != curr_rec.new_text_id THEN
temp_rec := getTextEditRecord(curr_rec.new_text_id);
--RETURN TABLE HERE
ELSE
-- No Recursive call directly return table
--RETURN TABLE HERE
END IF;
END IF;
--RETURN TABLE HERE
END;
$$ Language plpgsql;
Now I tried searching on google but could find that how to typecast record type to table type.
As in convert temp_rec to table type and return.
Doing this via a recursive function is the most inefficient way of doing it.
But anyhow, you can do something like this:
create or replace function get_text_edit_record(p_text_id integer)
RETURNS Table (
text_id integer,
text_details varchar,
new_text_id integer)
AS $$
DECLARE
curr_rec record;
BEGIN
select tm.text_id, tm.text_details, tm.new_text_id
into curr_rec
from text_master tm
where tm.text_id = p_text_id;
IF FOUND THEN
return query
select curr_rec.text_id, curr_rec.text_details, curr_rec.new_text_id;
--- IS DISTINCT FROM properly deals with NULL values
IF curr_rec.text_id IS DISTINCT FROM curr_rec.new_text_id THEN
return query
select *
from get_text_edit_record(curr_rec.new_text_id);
END IF;
END IF;
END;
$$ Language plpgsql;

"malformed array literal" error in PL/pgSQL

I got an error in PL/pgSQL:
"ERROR: malformed array literal: "2019-11-22 09:18:07.248537" DETAIL:
Array value must start with "{" or dimension information. CONTEXT:
PL/pgSQL function chargelengtth() line 11 at FOR over SELECT rows"
CREATE OR REPLACE FUNCTION chargelengtth() RETURNS text AS
$$
DECLARE chargetime integer;
DECLARE othertime integer;
DECLARE total integer;
DECLARE charge_length text[];
DECLARE chargestatus text;
DECLARE otherstatus text;
BEGIN
total := 0;
chargetime := 0;
FOR charge_length IN
SELECT * FROM table_name
LOOP
RAISE NOTICE 'Stuff: %', charge_length;
IF(charge_length[diagnostic] = 'Charging' AND chargetime = 0) THEN
chargetime := extract(epoch from charge_length[time]);
END IF;
IF(charge_length[diagnostic] != 'Charging') THEN
othertime := extract(epoch from charge_length[time]);
total := total + (othertime - chargetime);
chargetime := 0;
END IF;
END LOOP;
RETURN to_char(total * interval '1 sec','HH24h MIm');
END$$ LANGUAGE plpgsql;
SELECT * FROM chargelengtth()`
can you please share your knowledge to resolve this issue.
As documented in the manual you need to use a record variable when you want to loop over a SELECT statement:
CREATE OR REPLACE FUNCTION chargelengtth()
RETURNS text AS
$$
DECLARE
chargetime integer;
othertime integer;
total integer;
chargestatus text;
otherstatus text;
l_row record; --<< here
BEGIN
total := 0;
chargetime := 0;
FOR l_row IN SELECT * FROM table_name
LOOP
RAISE NOTICE 'Stuff: %', l_row;
IF (l_row.diagnostic = 'Charging' AND chargetime = 0) THEN
chargetime := extract(epoch from l_row.time);
END IF;
IF (l_row.diagnostic != 'Charging') THEN
othertime := extract(epoch from l_row.time);
total := total + (othertime - chargetime);
chargetime := 0;
END IF;
END LOOP;
RETURN to_char(total * interval '1 sec','HH24h MIm');
END$$ LANGUAGE plpgsql;
SELECT chargelengtth()
As far as I can tell, you can replace this with a single SELECT statement. Assuming the time column is a timestamp, I think the following is equivalent to what you are doing (except for the formatting of the interval at the end):
with ct (chargetime) as (
select time
from table_name
where diagnostic = 'Charging'
limit 1
)
select sum(t.time - ct.chargetime)
from the_table t
cross join ct
where t.diagnostic <> 'Charging'
The retrieval of the chargetime is a bit confusing as you seem to rely on some order of the rows, yet your query has no order by. The limit 1 without an order by does essentially the same, but if you want reproducible results you should really specify an order by (also for your FOR loop statement)

Create a NZPLSQL to find min(date) and a max(date) for a group of fields that serve as a key

I would like to know how I can execute a select from a procedure stored in Netezza.
I currently have the procedure under construction and I read that the raise notice serves to print on screen, however I do not know how (or if you can print a table in sql) just as if you were doing a query in traditional sql.
What the query is trying to do is that for a record,
COUNT (*) AS N_REPETITIONS,
CDS.CASO_PACIENTE_FK,
CDS.RESIVALITY_SPECIALITY_COD,
CDS.CASO_NIVEL_ATENCION_TIPO,
CDS.CASO_TIPO_PRESTACION_FK,
CDS.DIVING_DATE_DAY,
CDS.SALID_DATE_DAY
and that it gives me MIN(CDS.DERIVACION_FECHA_HORA) and MAX(CDS.SALIDA_FECHA_HORA) based on a key that are the fields.
If I have dates between MIN(CDS.DERIVACION_FECHA_HORA) and MAX(CDS.SALIDA_FECHA_HORA) for a CDS.CASO_PACIENTE_FK, i want that register added to number of register for each CDS.CASO_PACIENTE_FK
Here the code:
CREATE OR REPLACE PROCEDURE SP_ANALISIS_DUPLICADOS()
RETURNS REFTABLE(NETEZZA_PROD_LISTA_ESPERA_NOGES.LLAVE_CASOS_DUPLICADOS)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
num_args INTEGER;
I am just starting to work with Netezza.
Thank you in advance to whom you answer.
This is the code:
CREATE OR REPLACE PROCEDURE SP_ANALISIS_DUPLICADOS()
RETURNS REFTABLE(NETEZZA_PROD_LISTA_ESPERA_NOGES.LLAVE_CASOS_DUPLICADOS)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
num_args INTEGER;
typ oid;
idx INTEGER;
N_REPETICIONES NUMERIC(19);
CASO_PACIENTE_FK NUMERIC(19);
DERIVACION_ESPECIALIDAD_COD VARCHAR(255);
CASO_NIVEL_ATENCION_TIPO NUMERIC(19);
CASO_TIPO_PRESTACION_FK NUMERIC(19);
DERIVACION_FECHA_HORA TIMESTAMP;
SALIDA_FECHA_HORA TIMESTAMP;
--CURSOR FECHA_HORA_DERIVACION_MIN;
--CURSOR FECHA_HORA_SALIDA_MAX;
CURSOR CASO_PACIENTE_FK;
CURSOR DERIVACION_ESPECIALIDAD_COD;
CURSOR CASO_NIVEL_ATENCION_TIPO;
CURSOR CASO_TIPO_PRESTACION_FK;
--L_CONDITIONS VARCHAR(1000);
--Duplicados CNE MIN
BEGIN
num_args := PROC_ARGUMENT_TYPES.count;
FOR CASO_PACIENTE_FK IN
num_args := CASO_PACIENTE_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for i IN 0
CASO_PACIENTE_FK.count - 1 LOOP
typ := CASO_PACIENTE_FK(i);
idx := i+1;
FOR DERIVACION_ESPECIALIDAD_COD IN
num_args := DERIVACION_ESPECIALIDAD_COD.count;
for j IN 0
DERIVACION_ESPECIALIDAD_COD.count - 1 LOOP
typ := DERIVACION_ESPECIALIDAD_COD(j);
idx := j+1;
FOR CASO_NIVEL_ATENCION_TIPO IN
num_args := CASO_PACIENTE_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for k IN 0
CASO_NIVEL_ATENCION_TIPO.count - 1 LOOP
typ := CASO_NIVEL_ATENCION_TIPO(k);
idx := k+1;
FOR CASO_TIPO_PRESTACION_FK IN
num_args := CASO_TIPO_PRESTACION_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for a IN 0
CASO_TIPO_PRESTACION_FK.count - 1 LOOP
typ := CASO_TIPO_PRESTACION_FK(a);
idx := a+1;
--RAISE NOTICE ’argument $% is type % and has the value ’’%’’’,
--idx, typ, $idx;
F FLAG = 1 THEN
IF Fecha_Entrada = SELECT COUNT(*) AS N_REPETICIONES, MIN(CDS.DERIVACION_FECHA_HORA) AS FECHA_HORA_DERIVACION_MIN
FROM NETEZZA_PROD_SIGTE_DIARIO.SIG_CASO_DERIVACION_SALIDA CDS
WHERE (CDS.CASO_TIPO_PRESTACION_FK=1
OR CDS.CASO_TIPO_PRESTACION_FK=2)
--AND CDS.DERIVACION_FECHA_HORA >= CDS.DERIVACION_FECHA_HORA
GROUP BY CDS.CASO_PACIENTE_FK, CDS.DERIVACION_ESPECIALIDAD_COD, /*CDS.DERIVACION_ESTABLECIMIENTO_COD*/
CDS.CASO_NIVEL_ATENCION_TIPO, /*CDS.DERIVACION_FECHA_HORA,*/ CDS.CASO_TIPO_PRESTACION_FK, CDS.DERIVACION_FECHA_HORA,
CDS.SALIDA_FECHA_HORA
HAVING COUNT(*)>1
ORDER BY N_REPETICIONES, CDS.CASO_PACIENTE_FK, CDS.DERIVACION_FECHA_HORA, CDS.SALIDA_FECHA_HORA DESC
THEN Fecha_Entrada = 'TRUE';
ELSE FLAG = 0 THEN
Fecha_Entrada = 'FALSE'
F FLAG = 1 THEN
IF Fecha_Salida = SELECT COUNT(*) AS N_REPETICIONES, MAX(CDS.SALIDA_FECHA_HORA) AS FECHA_HORA_DERIVACION_MIN
FROM NETEZZA_PROD_SIGTE_DIARIO.SIG_CASO_DERIVACION_SALIDA CDS
WHERE (CDS.CASO_TIPO_PRESTACION_FK=1
OR CDS.CASO_TIPO_PRESTACION_FK=2)
--AND CDS.DERIVACION_FECHA_HORA >= CDS.DERIVACION_FECHA_HORA
GROUP BY CDS.CASO_PACIENTE_FK, CDS.DERIVACION_ESPECIALIDAD_COD, /*CDS.DERIVACION_ESTABLECIMIENTO_COD*/
CDS.CASO_NIVEL_ATENCION_TIPO, /*CDS.DERIVACION_FECHA_HORA,*/ CDS.CASO_TIPO_PRESTACION_FK, CDS.DERIVACION_FECHA_HORA,
CDS.SALIDA_FECHA_HORA
HAVING COUNT(*)>1
ORDER BY N_REPETICIONES, CDS.CASO_PACIENTE_FK, CDS.DERIVACION_FECHA_HORA, CDS.SALIDA_FECHA_HORA DESC
THEN Fecha_Salida = 'TRUE';
ELSE FLAG = 0 THEN
Fecha_Salida = 'FALSE'
END LOOP;
END LOOP;
END LOOP;
END LOOP;
RETURN REFTABLE;
RAISE NOTICE 'Hello, %', MYNAME; --Right Here must go the Println (Select field1, field2, etc.....)
--END;
--COMMIT;
END;
END_PROC;