No_data_found Error when the revevant data is there - oracle10g

I'm relatively new at PL/SQL so what I'm about to ask might sound somewhat elementary. I'm trying to run the following code and getting a No_Date_Found error when I shouldn't. I'm trying to execute a loop. I'm trying to select a set of values into variables based on another variable. The highlighted Bold line is where the error occurs. If I replace the v_tariff_code variable with a literal value in that line, the error doesn't occur anymore. Help please?
set serveroutput on
declare
v_remaining_arrears number := 0;
v_tariff_code date;
v_tariff_store date; --a variable that stores the value of the previous tariff while the above variable takes the next tariff code value
v_session_start_date date; --session start date
v_con_date date; --connection date
v_start_date date;
v_c_code varchar2(10);
v_cat_code number;
vunits number;
v_w_rate number;
v_s_rate number;
v_multiples number;
v_session_current varchar2(20);
v_counter number;
v_date1 date;
v_date2 date;
begin
v_c_code := 'D2203447';
v_session_current := 'JAN2014-FEB2014';
--V_SESSION_START_DATE := '01-JAN-14';
--v_date1 := v_session_start_date; --v_date1 would always begin as the session start date
--select v_session_start_date into v_date1 from dual;
--loop
select cat_code, units, con_date into v_cat_code, vunits, v_con_date from consumer where c_code = v_c_code;
select start_date into v_session_start_date from bill_session where session_code = v_session_current;
v_start_date := v_session_start_date;
loop
v_start_date := v_start_date - 30;
select MAX(tariff_code) INTO v_TARIFF_CODE from tariff where tariff_code < V_START_DATE;
**select w_rate, s_rate into v_w_rate, v_s_rate from cat_tariff where cat_code = v_cat_code and
tariff_code = v_tariff_code;**
v_remaining_arrears := v_remaining_arrears + ((round(v_w_rate *v_multiples*vunits) + round(v_s_rate * vunits)));
exit when v_start_date < v_con_date;
--DBMS_OUTPUT.PUT_LINE('ARREARS = ' || V_REMAINING_ARREARS);
end loop;
DBMS_OUTPUT.PUT_LINE('ARREARS = ' || V_REMAINING_ARREARS);
end;
Can you help?

select w_rate, s_rate
into v_w_rate, v_s_rate
from cat_tariff
where cat_code = v_cat_code and tariff_code = v_tariff_code;
This is an exact fetch which must return exactly one record. Is query returns zero or more then one records it causes the exception.
Try to add dbms_output.put_line(v_cat_code || ',' || v_tariff_code); before the query. Exactly before the exception this line will show problem values of v_cat_code and v_tariff_code.
Probable you wolud like to ignore this exception and process only cases which definitely return data. In this case you shold use BEGIN … EXCEPTION … END
begin
select w_rate, s_rate
into v_w_rate, v_s_rate
from cat_tariff
where cat_code = v_cat_code and tariff_code = v_tariff_code;
-- DO SOMETHING USEFULL WITH v_w_rate, v_s_rate
exception
when no_data_found then null;
when others then raise;
end;

Related

PostgreSQL PgAgent syntax error at or near DECLARE

Could someone tells me what is wrong please.
I try to create a job with using PgAgent with declaring some variables. When I run this code manually, it works successfully. But when I try to put this code in job step and save it, it throws me an error.
DO $$
DECLARE
start_date date;
dates date;
d SMALLINT;
counter integer := 0;
res date[];
treshold bigint;
BEGIN
TRUNCATE ditdemo.daily;
start_date:= now();
dates := start_date;
while counter <= 14 loop
dates := dates - INTERVAL '1 DAY';
select cal.is_holiday into d from ditdemo.calendar as cal where cal.calendardate = dates;
if d=0 then
res := array_append(res,dates);
counter := counter + 1;
end if;
/*
raise notice 'dates %', dates;
raise notice 'is holiday %', d;
raise notice 'result %', res;
*/
end loop;
insert into ditdemo.daily
select
time_bucket('1 day', j."timestamp") as day,
j.account,
count(*) as cnt
from ditdemo.jrnl as j
where
cast(j."timestamp" as date) in (select unnest(res)) AND
j.account not in (select account from ditdemo.user where is_service = 1)
group by day, j.account;
SELECT
round(PERCENTILE_CONT(0.95) WITHIN GROUP(ORDER BY d.cnt))
into treshold
FROM ditdemo.daily as d;
UPDATE ditdemo.calendar
SET daily_treshold = treshold
WHERE calendardate > start_date and calendardate <=(start_date::date + interval '7 day');
END $$;
It seems like PgAgent translates your code to another format, perhaps to string or something else and then can't parse it. To understand this try to:
Delete some special symbols from your code like brackets, quotes etc
Try to understand is it error from pgAgent or PostgreSQL
Good lucK!

Measure the time it takes to execute a PostgreSQL query

Based on Measure the time it takes to execute a t-sql query, how would one time several trials of a query in PostgreSQL?
A general outline would be
-- set up number of trials (say 1000)
SELECT CURRENT_DATE ; -- save start time
BEGIN
LOOP
-- execute query to be tested
END LOOP;
END;
SELECT CURRENT_DATE ; -- save end time
I.E. I want a PostgreSQL equivalent of the following TSQL code, taken from an answer by HumbleWebDev from the linked TSQL question: see [reference for code]
declare #tTOTAL int = 0
declare #i integer = 0
declare #itrs integer = 100
while #i < #itrs
begin
declare #t0 datetime = GETDATE()
--your query here
declare #t1 datetime = GETDATE()
set #tTotal = #tTotal + DATEDIFF(MICROSECOND,#t0,#t1)
set #i = #i + 1
end
select #tTotal/#itrs
-- your query here: Standard SQL queries such as Select * from table1 inner -- join table2, or executing stored procedure, etc.
Coming from an MSSQL background myself and now more often working in Postgres I feel your pain =)
The "trouble" with Postgres is that it supports only 'basic' SQL commands (SELECT, INSERT, UPDATE, CREATE, ALTER, etc...) but the moment you want to add logic (IF THEN, WHILE, variables, etc.) you need to switch to pl/pgsql which you can only use inside functions (AFAIK). From a TSQL POV there are quite some limitations and in fact, some things suddenly don't work anymore (or need to be done differently.. e.g. SELECT * INTO TEMPORARY TABLE tempTable FROM someTable will not work but CREATE TABLE tempTable AS SELECT * FROM someTable will)
Something I learned the hard way too is that CURRENT_TIMESTAMP (or Now()) will return the same value within a transaction. And since everything inside a function runs inside a transaction this means you have to use clock_timstamp()
Anyway, to answer your question, I think this should get you going:
CREATE OR REPLACE FUNCTION fn_test ( nbrOfIterations int)
RETURNS TABLE (iterations int, totalTime interval, secondsPerIteration int)
AS $$
DECLARE
i int;
startTime TIMESTAMP;
endTime TIMESTAMP;
dummy text;
BEGIN
i := 1;
startTime := clock_timestamp();
WHILE ( i <= nbrOfIterations) LOOP
-- your query here
-- (note: make sure to not return anything or you'll get an error)
-- example:
SELECT pg_sleep INTO dummy FROM pg_sleep(1);
i := i + 1;
END LOOP;
endTime := clock_timestamp();
iterations := nbrOfIterations;
totalTime := (endTime - startTime);
secondsPerIteration := (EXTRACT(EPOCH FROM endTime) - EXTRACT(EPOCH FROM startTime)) / iterations;
RETURN NEXT;
END;
$$ language plpgsql;
SELECT * FROM fn_test(5);
While the accepted answer is correct, this tweaking of it worked better for me. Again, I want to emphasize this extra answer below is based on the above answer, and it would not be possible without it. It just works better in my own situation to use the tweak I made below.
The answer below is indeed almost entirely based on the accepted answer. However, I changed how the return is used and also seconds to milliseconds:
----------------------------------------------------------------------------------------------------
-- fn__myFunction_Q.sql
----------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
-- DROP FUNCTION mySchema.fn__myFunction
--------------------------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION mySchema.fn__myFunction ( nbrOfIterations int)
RETURNS TABLE (iterations int, totalTime interval, millisecondsPerIteration int) -- interval --
AS $$
declare
i int;
startTime TIMESTAMP;
endTime TIMESTAMP;
-- dummy text;
iterations int;
millisecondsPerIteration int;
totalTime interval;
BEGIN
i := 1;
startTime := clock_timestamp();
WHILE ( i <= nbrOfIterations) LOOP
PERFORM /* Put your query here, replacing SELECT with PERFORM */
--------------------------------------------------------------------------------------------
--SELECT
-- YOUR QUERY HERE
-- ...
--------------------------------------------------------------------------------------------
i := i + 1; -- very important to increment loop counter, else one gets an infinite loop!!!
END LOOP;
endTime := clock_timestamp();
iterations := nbrOfIterations;
totalTime := (endTime - startTime);
millisecondsPerIteration := 1000 * (EXTRACT(EPOCH FROM endTime) - EXTRACT(EPOCH FROM startTime)) / iterations;
RETURN QUERY select iterations, totalTime, millisecondsPerIteration;
-- RETURNS TABLE (iterations int, totalTime interval, secondsPerIteration int) -- interval --
-- RETURN NEXT;
END;
$$ language plpgsql;
--------------------------------------------------------------------------------------------
To call this function, just use:
SELECT * from mySchema.fn__myFunction(1000) as ourTableResult;

How to get the value of var cast in this query postgresql?

I have the problem when create select insert loop in procedure postgresql. The problem is var cast('1000/1902/003' AS TEXT) always null. How to detect this variabel? i realy need this variabel.
I have try without casting but the parameter always read as integer.
BEGIN
FOR tx IN EXECUTE 'SELECT * FROM T_DataUpload2_FinalDetail WHERE dataupload2fd_id = CAST('|| 1000/1902/003 ||'AS TEXT)'
LOOP
data_cust := tx.DataUpload2FD_DistID;
data_dist := tx.DataUpload2FD_CustID;
RETURN NEXT;
END LOOP;
END;
Why dynamic SQL to begin with?
BEGIN
FOR tx IN SELECT *
FROM T_DataUpload2_FinalDetail
WHERE dataupload2fd_id = '1000/1902/003'
LOOP
data_cust := tx.DataUpload2FD_DistID;
data_dist := tx.DataUpload2FD_CustID;
RETURN NEXT;
END LOOP;
END;
The expression (without quotes) 1000/1902/003 means "1000 divided by 1902 divided by 3" the result would be 0,175... which is rounded to 0 (not null) because all values are integers and thus integer division is used.

Trying to create Postgres function

In the create screen of the Postgress GUI I'm getting a syntax error at the while statement. Language is plpgsql and return type is void. TIA
enter code here
begin
declare counter integer := 1;
declare CurrentDate Date := '1/1/2018';
while CurrentDate < '1/1/2019'
loop
insert into dimCalendar select CurrentDate, EXTRACT(DOW FROM
current_date), EXTRACT(DOY FROM current_date);
CurrentDate := CurrentDate + 1;
end loop
end
Next time, try to include all the code please
Always use ISO date format, unless you have a reason not to
declare goes before begin
You're missing a semi-colon after "end loop"
Use snake_case in PostgreSQL please
create or replace function foo() returns void as $$
declare
counter integer := 1;
curr_date date := '2018-01-01';
begin
while curr_date < '2019-01-01' loop
raise notice 'curr_date: %', curr_date;
curr_date := curr_date + 1;
end loop;
end
$$ language plpgsql;

postgresql to_timestamp accepts invalid dates by design

I'm trying to validate strings to timestamps from several CSVs and simply casting them to timestamptz will fail due to the impossibility of forcing an unique datetime format:
select '10/31/2010'::timestamp --fail due to "different datestyle"
select '31/10/2010'::timestamp --works
I thought to_timestamp() would do the trick, but something like:
select to_timestamp('31/02/2014 14:30', 'DD/MM/YYYY HH24:MI');
will return "2014-03-03 14:30:00-05" instead of throwing an exception
So I thought of using this approach, that reverts back the output to text using to_char and comparing it with the original input, but a mask like 'DD/MM/YYYY HH24:MI' will cast "06/03/2014 0:06" to "06/03/2014 00:06", the strings are different!
CREATE OR REPLACE FUNCTION to_timestamp_safe(IN p_date text, IN p_format text, OUT r_date timestamp without time zone)
RETURNS timestamp without time zone AS
$BODY$
BEGIN
r_date = TO_TIMESTAMP(p_date, p_format);
IF TO_CHAR(r_date, p_format) != p_date THEN
RAISE EXCEPTION 'Input date % does not match output date %', p_date, r_date;
END IF;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
The following example fails where it should work:
select to_timestamp_safe ('06/03/2014 0:06', 'DD/MM/YYYY HH24:MI');
output:
ERROR: Input date 06/03/2014 0:06 does not match output date 2014-03-06 00:06:00
SQL state: P0001
Is there a smart way of safely validate strings to timestamptz without the above pitfalls?
The FM modifier did the trick (thanks #ErwinBrandstetter) and it's indeed kind of a general solution because we came up with this idea of creating a profile for each type of csv and the date format mask can be stored there for the columns that are date/time, most part of the columns go to an hstore column anyway and we need to keep their types stored somewhere. So I was able to create something like this, where _colmask is the datetime format mask that may or may not have a FM modifier. Then I simply perform this function against my staging table (looping for each column)
CREATE OR REPLACE FUNCTION validate_column_datatype(_colvalue text, _colname text, _type text, _colmask text)
RETURNS void AS
$BODY$
BEGIN
declare
-- error stack
_returned_sqlstate text := null;
_column_name text := null;
_constraint_name text := null;
_pg_datatype_name text := null;
_message_text text := null;
_table_name text := null;
_schema_name text := null;
_pg_exception_detail text := null;
_pg_exception_hint text := null;
_pg_exception_context text := null;
BEGIN
IF _type = 'timestamptz' then
IF TO_CHAR(TO_TIMESTAMP(_colvalue, _colmask), _colmask) != _colvalue THEN
RAISE EXCEPTION 'Input date % does not match output date', _colvalue;
END IF;
ELSEIF _type = 'timestamp' then
IF TO_CHAR(TO_TIMESTAMP(_colvalue, _colmask), _colmask) != _colvalue THEN
RAISE EXCEPTION 'Input date % does not match output date', _colvalue;
END IF;
ELSEIF _type = 'numeric' then
perform _colvalue::numeric;
ELSEIF _type = 'integer' then
perform _colvalue::integer;
-- other types
END IF;
-- exception occurs
EXCEPTION WHEN OTHERS THEN
get stacked diagnostics
_returned_sqlstate = RETURNED_SQLSTATE,
_column_name = COLUMN_NAME,
_constraint_name = CONSTRAINT_NAME,
_pg_datatype_name = PG_DATATYPE_NAME,
_message_text = MESSAGE_TEXT,
_table_name = TABLE_NAME,
_schema_name = SCHEMA_NAME,
_pg_exception_detail = PG_EXCEPTION_DETAIL,
_pg_exception_hint = PG_EXCEPTION_HINT,
_pg_exception_context = PG_EXCEPTION_CONTEXT;
_message_text := -- write something meaningful
_pg_exception_detail = -- write something meaningful
_pg_exception_hint := -- write something meaningful
-- log to something
END;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;