im using this code and I had 1 problem, the o_besteltijden cursor has this as output: 01-06-15 ( a date), but I prefer output like: 16:00 (so only time) Or 01-06-15 16:00:00 (date + time). is that possible?
SET SERVEROUTPUT ON;
CREATE OR REPLACE TYPE t_openingstijd IS TABLE OF DATE;
CREATE OR REPLACE PROCEDURE zoekMogelijkeBesteltijden(p_winkelId IN INTEGER, p_datum IN DATE, p_periode IN INTEGER DEFAULT 21, p_bezorgen IN BOOLEAN,
o_open OUT BOOLEAN, o_besteltijden OUT SYS_REFCURSOR)
AS
v_foundWinkel NUMBER := 0;
v_winkel Winkel%ROWTYPE;
TYPE arrayVarchar IS VARRAY(7) OF VARCHAR2(2);
v_dagen arrayVarchar := arrayVarchar('ma', 'di', 'wo', 'do', 'vr', 'za', 'zo');
CURSOR v_openingstijden(p_id IN NUMBER, p_dag IN VARCHAR2) IS
SELECT * FROM Openingstijd
WHERE winkel_id = p_id
AND dag = p_dag;
v_besteltijden t_openingstijd := t_openingstijd();
v_eindUur NUMBER := 0;
v_eindMinuten NUMBER := 0;
v_beginUur NUMBER := 0;
v_beginMinuten NUMBER := 0;
-- Exceptions
v_winkelNotFound EXCEPTION;
BEGIN
-- Kijken of winkel wel bestaat.
SELECT COUNT(1) INTO v_foundWinkel
FROM Winkel
WHERE id = p_winkelId;
IF(v_foundWinkel = 0) THEN
RAISE v_winkelNotFound;
END IF;
-- Alle data krijgen van de winkel. Exclusief de openingstijden, producten en coupons.
SELECT * INTO v_winkel
FROM Winkel
WHERE id = p_winkelId;
FOR i_tijd IN v_openingstijden(p_winkelId,
v_dagen(TO_NUMBER(TO_CHAR(p_datum, 'D')))) LOOP
-- Instellen van huidige tijd om mee door te lopen.
v_eindUur := TO_NUMBER(TO_CHAR(i_tijd.gesloten, 'HH24'));
IF(p_bezorgen = true) THEN
IF(TO_NUMBER(TO_CHAR(i_tijd.gesloten, 'MI')) < 30) THEN
IF(TO_NUMBER(TO_CHAR(i_tijd.gesloten, 'MI')) < 15) THEN
v_eindMinuten := 30;
ELSE
v_eindMinuten := 45;
END IF;
v_eindUur := v_eindUur - 1;
ELSE
IF(TO_NUMBER(TO_CHAR(i_tijd.gesloten, 'MI')) < 45) THEN
v_eindMinuten := 0;
ELSE
v_eindMinuten := 15;
END IF;
END IF;
ELSE
IF(TO_NUMBER(TO_CHAR(i_tijd.gesloten, 'MI')) < 15) THEN
v_eindMinuten := 45;
v_eindUur := v_eindUur - 1;
ELSE
IF(TO_NUMBER(TO_CHAR(i_tijd.gesloten, 'MI')) < 30) THEN
v_eindMinuten := 0;
ELSIF(TO_NUMBER(TO_CHAR(i_tijd.gesloten, 'MI')) < 45) THEN
v_eindMinuten := 15;
ELSE
v_eindMinuten := 30;
END IF;
END IF;
END IF;
-- Begin tijd berekenen
v_beginUur := TO_NUMBER(TO_CHAR(i_tijd.open, 'HH24'));
IF(TO_NUMBER(TO_CHAR(i_tijd.open, 'MI')) < 15) THEN
v_beginMinuten := 0;
ELSIF(TO_NUMBER(TO_CHAR(i_tijd.open, 'MI')) < 30) THEN
v_beginMinuten := 15;
ELSIF(TO_NUMBER(TO_CHAR(i_tijd.open, 'MI')) < 45) THEN
v_beginMinuten := 30;
ELSE
v_beginMinuten := 45;
END IF;
-- Eerste uur vol maken.
IF(v_beginMinuten != 0) THEN
FOR i IN 1 .. ((60 - v_beginMinuten) / 15) LOOP
v_besteltijden.extend;
v_besteltijden(v_besteltijden.count) := TO_DATE( v_beginUur || ':' || v_beginMinuten * i, 'HH24:MI');
END LOOP;
v_beginUur := v_beginUur + 1;
v_beginMinuten := 0;
END IF;
-- Tot het laatste uur volmaken.
IF(v_beginUur != v_eindUur) THEN
FOR x IN 1 .. (v_eindUur - v_beginUur) LOOP
FOR i IN 1 .. 4 LOOP
v_besteltijden.extend;
IF(i = 1) THEN
v_besteltijden(v_besteltijden.count) := TO_DATE( v_beginUur || ':00', 'HH24:MI');
ELSE
v_besteltijden(v_besteltijden.count) := TO_DATE( v_beginUur || ':' || 15 * (i - 1), 'HH24:MI');
END IF;
END LOOP;
v_beginUur := v_beginUur + 1;
END LOOP;
END IF;
-- Laatste uur ook maar eens volmaken.
IF(v_beginUur = v_eindUur AND v_eindMinuten >= v_beginMinuten) THEN
FOR i IN 1 .. (v_eindMinuten / 15) + 1 LOOP
v_besteltijden.extend;
IF(i = 1) THEN
v_besteltijden(v_besteltijden.count) := TO_DATE( v_beginUur || ':00', 'HH24:MI');
ELSE
v_besteltijden(v_besteltijden.count) := TO_DATE( v_beginUur || ':' || 15 * (i - 1), 'HH24:MI');
END IF;
END LOOP;
END IF;
END LOOP;
-- Data terug in output cursor.
OPEN o_besteltijden FOR
SELECT * FROM TABLE(CAST(v_besteltijden AS t_openingstijd));
-- Is de winkel momenteel open?
FOR i_openingstijd IN v_openingstijden(p_winkelId,
v_dagen(TO_NUMBER(TO_CHAR(SYSDATE, 'D')))) LOOP
IF(o_open = true) THEN
IF(TO_NUMBER(TO_CHAR(SYSDATE, 'HH24MI')) >= TO_NUMBER(TO_CHAR(i_openingstijd.open, 'HH24MI'))
AND TO_NUMBER(TO_CHAR(SYSDATE, 'HH24MI')) <= TO_NUMBER(TO_CHAR(i_openingstijd.gesloten, 'HH24MI'))) THEN
o_open := true;
ELSE
o_open := false;
END IF;
END IF;
END LOOP;
EXCEPTION
WHEN v_winkelNotFound THEN
dbms_output.put_line('Winkel niet gevonden.');
END;
/
The problem here seems to be as follows:
You are setting o_besteltijden as a value selected from CAST(v_besteltijden AS t_openingstijd) where v_besteltijden is calculated from to_date
t_openingstijd is a table of date type (CREATE OR REPLACE TYPE t_openingstijd IS TABLE OF DATE;). So your answer will be in date format.
Instead if you need the timestamp type you have to make changes to these variables and get timestamp stored in them to get the output as desired date+time or time through formatting.
Related
Converted a Standalone Procedure from Oracle to Postgres but not sure why there is an run error even the code is successfully compiled
Converted the below code from Oracle to Postgres
CREATE OR REPLACE FUNCTION ssp2_pcat.pop_hoa_contracts_for_prod(
)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
DECLARE
C1 CURSOR for
SELECT MARKET_CODE, CONTRACT_COMBO_ID, COUNT(*) FROM
ssp2_pcat.VPF_HOA_CONTRACTS_FOR_PROD A
WHERE start_Date IN
(SELECT MAX(start_date)
FROM VPF_HOA_CONTRACTS_FOR_PROD b
WHERE A.MARKET_CODE = b.MARKET_CODE
AND A.CONTRACT_COMBO_ID = b.CONTRACT_COMBO_ID
AND A.CONTRACT_ID = B.CONTRACT_ID
AND b.start_date <= current_date
AND b.end_date > current_date )
GROUP BY MARKET_CODE, CONTRACT_COMBO_ID
ORDER BY MARKET_CODE, CONTRACT_COMBO_ID;
C2 CURSOR(iMktCode VARCHAR, iCombo integer) for
SELECT MARKET_CODE, CONTRACT_COMBO_ID, CONTRACT_ID
FROM ssp2_pcat.VPF_HOA_CONTRACTS_FOR_PROD A
WHERE start_Date IN
(SELECT MAX(start_date)
FROM ssp2_pcat.VPF_HOA_CONTRACTS_FOR_PROD b
WHERE A.MARKET_CODE = b.MARKET_CODE
AND A.CONTRACT_COMBO_ID = b.CONTRACT_COMBO_ID
AND A.CONTRACT_ID = B.CONTRACT_ID
AND b.start_date <= current_date
AND b.end_date > current_date )
AND MARKET_CODE = iMktCode
AND CONTRACT_COMBO_ID = iCombo
ORDER BY MARKET_CODE, CONTRACT_COMBO_ID, START_DATE;
Contracts VARCHAR(32000);
Contract_Val1 VARCHAR(4000) := NULL;
Contract_Val2 VARCHAR(4000) := NULL;
Contract_Val3 VARCHAR(4000) := NULL;
Contract_Val4 VARCHAR(4000) := NULL;
Contract_Val5 VARCHAR(4000) := NULL;
Contract_Val6 VARCHAR(4000) := NULL;
Contract_Val7 VARCHAR(4000) := NULL;
Contract_Val8 VARCHAR(4000) := NULL;
Num INTEGER;
Cont_Num INTEGER;
l_start TIMESTAMP := clock_timestamp();
l_end TIMESTAMP := clock_timestamp();
Time_Taken VARCHAR(20);
i record;
j record;
BEGIN
l_start := clock_timestamp();
DELETE FROM ssp2_pcat.HOA_CONTRACTS_KH;
FOR i IN C1 LOOP
BEGIN
Num := 0;
Contracts := NULL;
Cont_Num := 1;
FOR j IN C2 (i.MARKET_CODE, i.CONTRACT_COMBO_ID) LOOP
Num := Num + 1;
IF Num = 1 THEN
Contracts := '|' || j.CONTRACT_ID || '|';
ELSE
IF LENGTH(Contracts || j.CONTRACT_ID || '|') > 4000 THEN
PERFORM ssp2_pcat.Assign (Cont_Num, SUBSTRING(Contracts, 1,
LENGTH(Contracts)-1));
Num := 1;
Contracts := '|' || j.CONTRACT_ID || '|';
Cont_Num := Cont_Num + 1;
ELSE
Contracts := Contracts || j.CONTRACT_ID || '|';
END IF;
END IF;
END LOOP;
PERFORM ssp2_pcat.Assign (Cont_Num, Contracts);
IF Cont_Num > 5 THEN
raise notice'%', ('MARKET_CODE: ' || i.MARKET_CODE || ', CONTRACT_COMBO_ID: ' || i.CONTRACT_COMBO_ID || ' has more than 32K in size. These Contracts are left out: ' || Contracts);
END IF;
INSERT INTO HOA_CONTRACTS_KH
(
MARKET_CODE,
CONTRACT_COMBO_ID,
CONTRACT_ID,
CONTRACT_ID2,
CONTRACT_ID3,
CONTRACT_ID4,
CONTRACT_ID5,
LAST_UPDATED
)
VALUES
(
i.MARKET_CODE,
i.CONTRACT_COMBO_ID,
Contract_Val1,
Contract_Val2,
Contract_Val3,
Contract_Val4,
Contract_Val5,
CURRENT_TIMESTAMP::TIMESTAMP(0)
);
Contract_Val1 := NULL;
Contract_Val2 := NULL;
Contract_Val3 := NULL;
Contract_Val4 := NULL;
Contract_Val5 := NULL;
Contract_Val6 := NULL;
Contract_Val7 := NULL;
Contract_Val8 := NULL;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
raise notice'%', ('1) POP_HOA_CONTRACTS_FOR_PROD: ' || SQLERRM);
END;
END LOOP;
RAISE NOTICE 'Function excution time Took: %', l_start;
RAISE NOTICE 'Function excution time Took: %',l_end-l_start;
SELECT l_end-l_start INTO Time_Taken;
raise notice'%',('POP_HOA_CONTRACTS_FOR_PROD Took: ' || Time_Taken );
EXCEPTION
WHEN OTHERS THEN
raise notice'%', ('2) POP_HOA_CONTRACTS_FOR_PROD: ' || SQLERRM);
END;
$BODY$;
The code is compiled successfully, but giving a run time error as follows,
NOTICE: 2) POP_HOA_CONTRACTS_FOR_PROD: cannot begin/end transactions in PL/pgSQL
Debugged the whole code and looks like still I'm unable to identify the issue, can any one help me in making me understand more about Postgres as I'm new to this Database. Found out in unit testing that its not calling the assign function mentioned in the code,
Please suggest me how I can achieve this (From Oracle to Postgres).
For these following functions.
ENCRYPTION
PROCEDURE sp_encrypt
(
i_ustring IN VARCHAR2,
o_estring OUT VARCHAR2
)
IS
crypt_part VARCHAR2(10);
BEGIN
o_estring := NULL;
FOR pos in 1..length(i_ustring)
loop
crypt_part := NULL;
crypt_part := utl_raw.bit_xor(HEXTORAW(utl_raw.cast_to_raw(SUBSTR(i_ustring,pos,1))),HEXTORAW(fn_num_to_hex(MOD(pos,256))));
if length(crypt_part)<2
then
crypt_part := '0'||crypt_part;
end if;
o_estring := o_estring||crypt_part;
end loop;
o_estring := utl_raw.cast_to_varchar2(utl_raw.reverse(utl_raw.cast_to_raw(o_estring)));
END sp_encrypt;
DECRYPTION
PROCEDURE sp_decrypt
(
i_estring IN VARCHAR2,
o_ustring OUT VARCHAR2
)
IS
estring VARCHAR2(32767);
crypt_part VARCHAR2(10);
pos NUMBER;
BEGIN
estring := utl_raw.cast_to_varchar2(utl_raw.reverse(utl_raw.cast_to_raw(i_estring)));
pos := 1;
FOR cnt in 1..length(estring)/2
loop
crypt_part := NULL;
crypt_part := CHR(fn_hex_to_num(utl_raw.bit_xor(HEXTORAW(SUBSTR(estring,pos,2)),HEXTORAW(fn_num_to_hex(MOD(NVL(LENGTH(o_ustring),0)+1,256))))));
o_ustring := o_ustring||crypt_part;
pos:=pos+2;
end loop;
END sp_decrypt;
ERROR: missing "LOOP" at end of SQL expression
CONTEXT: compilation of PL/pgSQL function "player_height_rank" near line 9
Here is my code:
CREATE OR REPLACE FUNCTION player_height_rank (irstname VARCHAR, lastname VARCHAR) RETURNS int AS $$
DECLARE
rank INTEGER := 0;
offset INTEGER := 0;
tempValue INTEGER := NULL;
r record;
BEGIN
FOR r IN SELECT ((p.h_feet * 30.48) + (p.h_inches * 2.54)) AS height, p.firstname, p.lastname
FROM players p
WHERE p.firstname = $1 AND p.lastname = $2;
ORDER BY ((p.h_feet * 30.48) + (p.h_inches * 2.54)) DESC, p.firstname, p.lastname
LOOP
IF r.height = tempValue then
offset := offset + 1;
ELSE
rank := rank + offset + 1;
offset := 0;
tempValue := r.height;
END IF;
IF r.lastname = $4 AND r.lastname = $3 THEN
RETURN rank;
END IF;
END LOOP;
-- not in DB
RETURN -1;
END;
$$ LANGUAGE plpgsql;
In your WHERE clause semicolon is superfluous
WHERE p.firstname = $1 AND p.lastname = $2; -- delete semicolon
correct that part and try again.
I would need a Oracle pl/sql program to check if a 9 digit number is in sequence( for e.g 0123456789,123456789,345678901, 456789012, 567890123 etc).
declare
type t_num is table of number;
t t_num := t_num(1, 234, 5678, 43231567);
first_digit number;
leng number;
numb number;
begin
for i in t.first .. t.last loop
first_digit := substr(t(i), 1, 1);
leng := length(t(i));
numb := first_digit;
for j in 1 .. leng - 1 loop
numb := numb || (first_digit + j);
end loop;
if t(i) = numb then
dbms_output.put_line(numb);
end if;
end loop;
end;
I'm playing around with plpgsql and have put together a function that assembles a dynamic query. I've tested it and it executes (i've included a test wrapper to output the assembled query).
Where I'm stumbling is capturing the output of the EXECUTE command once it's run, because I'd like to return some or all of the values, depending on the nature of the dynamic query. I've set up a type userprofile, and have the setProfileDynamic function return this type.
With the full complement of parameters, the output checks out (except for the second query, more on that in a bit). But when some of the parameters are missing (ie not all the user preferences are updated, only one eg: measuresystem) then the output is corrupted, so that measuresystem_id might appear as username in the output.
Secondly, is how to get the result of the second query (updateDefaultMealplan) into the userprofile type (where the columns mealplan_id and mealplan_name are waiting patiently). Currently this query returns into mp_id (and mp_name is filled from the _values array if 'defaultmealplan' key is present).
I'm quite new to this and I might be trying to do too much in one function, and I might be doing it completely the wrong way, so I don't mind whatever corrections might come to pass.
The userprofile type:
DROP TYPE IF EXISTS userprofile CASCADE;
CREATE TYPE userprofile AS (
username text,
measuresystem_id int,
blanksymbol_id int,
mealplan_id int,
mealplan_name text
);
The main function
DROP FUNCTION IF EXISTS setProfileDynamic (int, text, text[], text[]);
CREATE OR REPLACE FUNCTION setProfileDynamic (_userid int, _token text, _keys text[], _values text[])
RETURNS userprofile AS $$
DECLARE
_query text;
numkeys int;
i int;
_update text[];
_from text[];
_where text[];
_return text[];
_into text[];
test text[];
up userprofile;
mp_name text;
mp_id int;
u text;
f text;
w text;
r text;
c_update int := 1;
c_from int := 1;
c_where int := 3;
c_return int := 1;
runupdate boolean := false; --bc passing default mealplan through this fn too.
changedefaultmp boolean := false;
BEGIN
test[1] := 'users.id';
test[2] := 'users.token';
test[3] := _userid;
test[4] := _token;
numkeys := array_length(_keys, 1);
raise notice 'numkeys = %', numkeys;
_where[1] := test[1] || ' = ' || quote_literal(test[3]);
_where[2] := test[2] || ' = ' || quote_literal(test[4]);
--raise notice '_where[1] = %', _where[1];
--raise notice '_where[2] = %', _where[2];
for i in 1..numkeys loop
raise notice 'keys[%] = %', i, _keys[i];
CASE _keys[i]
WHEN 'email' THEN
runupdate := true;
_update[c_update] := quote_ident(_keys[i]) || ' = ' || quote_literal(_values[i]);
c_update := c_update + 1;
WHEN 'password' THEN
runupdate := true;
_update[c_update] := quote_ident(_keys[i]) || ' = ' || quote_literal(_values[i]);
c_update := c_update + 1;
WHEN 'username' THEN
runupdate := true;
_update[c_update] := quote_ident(_keys[i]) || ' = ' || quote_literal(_values[i]);
c_update := c_update + 1;
_return[c_return] := quote_ident(_keys[i]);
c_return := c_return + 1;
WHEN 'measuresystem' THEN
runupdate := true;
_update[c_update] := 'measuresystem_id = ms.id';
c_update := c_update + 1;
_from[c_from] := 'measuresystem as ms';
c_from := c_from + 1;
_where[c_where] := 'ms.name = ' || quote_literal(_values[i]);
c_where := c_where + 1;
_return[c_return] := 'ms.id';
c_return := c_return + 1;
WHEN 'blanksymbol' THEN
runupdate := true;
_update[c_update] := 'blanksymbol_id = bs.id';
c_update := c_update + 1;
_from[c_from] := 'blanksymbol as bs';
c_from := c_from + 1;
_where[c_where] := 'bs.name = ' || quote_literal(_values[i]);
c_where := c_where + 1;
_return[c_return] := 'bs.id';
c_return := c_return + 1;
ELSE
changedefaultmp := true;
mp_name := _values[i];
END CASE;
end loop;
u := 'UPDATE users SET ' || array_to_string(_update, ', ');
f := 'FROM ' || array_to_string(_from, ', '); --if a_t_s is null, the whole f is null and not included so no error
w := 'WHERE ' || array_to_string(_where, ' AND ');
r := 'RETURNING ' || array_to_string(_return, ', ');
raise notice 'u = %', u;
raise notice 'f = %', f;
raise notice 'w = %', w;
raise notice 'r = %', r;
_query = concat_ws(' ', u, f, w, r);
raise notice '_query = %', _query;
IF runupdate THEN
if r IS NULL THEN
EXECUTE _query;
ELSE
EXECUTE _query INTO up;
END IF;
END IF;
IF changedefaultmp THEN
SELECT into mp_id updateDefaultMealplan(_userid, mp_name);
END IF;
return up;
END
$$ LANGUAGE PLPGSQL;
This is the wrapper function where you can see the query generated for different inputs:
DROP FUNCTION IF EXISTS T ();
CREATE OR REPLACE FUNCTION T ()
RETURNS setof userprofile AS $$
declare
_keys text[];
_values text[];
_userid int := 1;
_token text := 'beet';
begin
_keys := ARRAY['email', 'password', 'username', 'measuresystem', 'blanksymbol', 'defaultmealplan'];
_values := ARRAY['s#p.com', 'secret', 'myname', 'metric', '?', 'new'];
--_keys := ARRAY['email', 'blanksymbol'];
--_values := ARRAY['k#d.com', '[]'];
return query
SELECT * from setProfileDynamic(_userid, _token, _keys, _values);
end
$$ LANGUAGE PLPGSQL;
I realize it's a lot of code to get through, I hope the T function helps to clarify things. 'email' and 'password' params are not returning. 'defaultmealplan' triggers the second query. Any of 'username', 'measuresystem', 'blanksymbol' or 'defaultmealplan' should return a value into the userprofile type. Thanks for any forthcoming feedback.
the basic issue is so your dynamic query doesn't returns all necessary columns, second issue - you probably expecting, but it is not valid expectation, so records are assigned with respecting field' names. But when you assign some values to some composite type, postgres dosn't check name - only order is important. So you have to use NULLs for filling gaps and return all field.
you can simplify your code with array concating
DECLARE _return_cols text[] = '{}';
BEGIN
_return_cols := _return_cols || quote_ident('some_column');
_return_cols := _return_cols || quote_ident('some_other_column');
...