"malformed array literal" error in PL/pgSQL - sql

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)

Related

Nested loop over selected row_to_json result

I am trying to do a nested loop on the result of row_to_json() but getting below error.
DO
$BODY$
DECLARE
js jsonb := '{"ddd525b9-e47c-46ac-ab55-e0db93338ff3": "ddd525b9-e47c-46ac-ab55-e0db93338ff4", "550e8400-e29b-41d4-a716-446655440000": "e15c6fe3-000c-4c69-9c41-9e241323cbe5", "0ba26163-bf75-40d2-8526-172229604d95": "7ed83dbc-3895-490c-8a16-9842cfe85e28"}';
i record;
BEGIN
FOR i IN SELECT * FROM jsonb_each_text(js)
loop
DECLARE
orgs jsonb := (SELECT row_to_json(t) FROM (Select parent_id from my_schema.my_table o where o.id != i.value) t);
j record;
for j in select * from jsonb_each_text(orgs)
loop
RAISE NOTICE 'key %', j.key;
RAISE NOTICE 'value %', j.value;
end loop;
END LOOP;
END;
$BODY$;
Error:
SQL Error [42601]: ERROR: syntax error at or near "for"
Position: 506
You forgot the BEGIN and END around your second loop.
DO
$do$
DECLARE
js jsonb := '{"ddd525b9-e47c-46ac-ab55-e0db93338ff3": "ddd525b9-e47c-46ac-ab55-e0db93338ff4", "550e8400-e29b-41d4-a716-446655440000": "e15c6fe3-000c-4c69-9c41-9e241323cbe5", "0ba26163-bf75-40d2-8526-172229604d95": "7ed83dbc-3895-490c-8a16-9842cfe85e28"}';
i record;
BEGIN
FOR i IN (SELECT * FROM jsonb_each_text(js)) LOOP
DECLARE
orgs jsonb := (SELECT row_to_json(t) FROM (Select parent_id from my_schema.my_table o where o.id != i.value) t);
j record;
BEGIN
FOR j IN (select * from jsonb_each_text(orgs)) LOOP
RAISE NOTICE 'key %', j.key;
RAISE NOTICE 'value %', j.value;
END LOOP;
END;
END LOOP;
END;
$do$;

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
;

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;

Oracle sql to pgsql with table() function issue

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;

Dynamic SQL - ORACLE

I have the following procedure, which does not compile correctly, because it refers to non existing objects (table does not exist)
Here is only a section of the code (i used generic names for tables and columns):
DECLARE
C INTEGER := 0;
BEGIN
SELECT COUNT(1) INTO C FROM USER_TABLES WHERE TABLE_NAME = 'MY_TABLE';
IF C > 0 THEN
DECLARE
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE = 1;
BEGIN
FOR prec IN c_maps LOOP
some code...;
END LOOP;
EXECUTE IMMEDIATE 'some code..';
END;
END IF;
END;
/
I don't know how to write this statement dynamically, since the table "MY_TABLE" does not exist:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE =1;
I also tried to write it like:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM (Select 'MY_TABLE' from dual) WHERE ACTIVE = 1;
However, than it refers to the column "ACTIVE" which also does not exist at compile time...It is possible to write the whole procedure inside "execute immediate" - block? I have tried different variants, however without success
You may need to open the cursor in a different way, so that the non existing table is only referred in dynamic SQL; for example:
declare
c integer := 0;
curs sys_refcursor;
v1 number;
v2 number;
begin
select count(1)
into c
from user_tables
where table_name = 'MY_TABLE';
if c > 0
then
open curs for 'select column_name1, column_name2 from my_table where active = 1';
loop
fetch curs into v1, v2;
exit when curs%NOTFOUND;
dbms_output.put_line(v1 || ' - ' || v2);
end loop;
else
dbms_output.put_line('The table does not exist');
end if;
end;
/