INTERVAL add i day is not working in postgresql - postgresql

I have a table like this:
CREATE TABLE DateInsert(
DateInsert timestamp without time zone,
DateInt integer NOT NULL
);
I want insert list day from 2018-01-01 to 2045-05-18 but it give me an erro
"invalid input syntax for type interval:"
CREATE OR REPLACE FUNCTION insertdate() RETURNS integer AS $$
DECLARE i integer := 0;
d timestamp without time zone := '2018-01-01';
di integer := 0;
BEGIN
while i <10000
LOOP
d := d + INTERVAL ''+ i::character varying + ' day';
di := to_char(d , 'yyyymmdd')::int;
insert into DateInsert(DateInsert,DateInt) values(d, di);
i := i+1;
END LOOP ;
return i;
END;
$$ LANGUAGE plpgsql;
How can I insert to db with timestamp increase 1 in n day loop?
Code In sql server has been working.
declare #i int=0
declare #d datetime
declare #di int = 0
while #i <10000
begin
set #d = DATEADD(DAY, #i, '2018-01-01')
set #di = cast(CONVERT(VARCHAR(10), #d, 112) as int)
insert into DateInsert(DateInsert,DateInt) values(#d, #di)
set #i = #i+1
end

The concatenation operator is || not +. And the prefixed form doesn't seem to like anything else than literals. But you can cast the concatenation expression.
So changing
...
d := d + INTERVAL ''+ i::character varying + ' day';
...
to
...
d := d + (i || ' day')::interval;
...
should work for you.

Related

Store multiple columns result query in array variable

I would like to store the result of a query in a arraylist of a custom type.
The created type is:
create type statement_part AS (
statementid integer,
statement_value_date timestamp,
statement_last_transmission timestamp);
and the procedure code:
CREATE OR REPLACE FUNCTION public.get_bank_statements_downloadable_state
(
folderID integer,
date1 timestamp without time zone,
date2 timestamp without time zone
)
RETURNS bank_day[]
LANGUAGE plpgsql
AS $get_bank_statements_downloadable_state$
declare
statements_part_array statement_part[];
temp_statements_part_array statement_part[];
statements_part_element statement_part;
bank_day_array bank_day[];
bank_day_element bank_day;
nb_days integer;
begin
select array(bast_id::integer), array(bast_valuedate), array(bast_lasttransmission)
from bast_bankstatement
inner join baac_bankaccount
on bast_baac_id = baac_bankaccount.baac_id
where baac_folder_id = folderid
and (bast_valuedate between date1 and date2)
and bast_state = 1
into statements_part_array;
foreach statements_part_element in array statements_part_array loop
RAISE NOTICE 'Array list(%)', statements_part_array;
end loop;
nb_days := (select * from DATE_PART('day', AGE(date1, date2)) AS days);
for dayCounter in 0..nb_days-1 loop
--on met dans notre tableau temporaire les relevés banque valides du jour du cabinet
temp_statements_part_array :=
(
select *
from statements_part_array
where statements_part_array.statement_value_date = date1+dayCounter
);
if count(temp_statement_list) = 0 then
bank_day_element := (date1+dayCounter,0);
else
bank_day_element := (date1+dayCounter,2);
foreach statements_part_element in array temp_statements_part_array
loop
if statements_part_element.statement_last_transmission is null then
bank_day_element := (date1+dayCounter,1);
EXIT;
end if;
end loop;
end if;
bank_day_array := array_append(bank_day_array,bank_day_element);
end loop;
return bank_day_array;
END;
$get_bank_statements_downloadable_state$;
But it fails . It says the subquery must return only one column when I execute it.
I solved the issue by using
select array(select row(bast_id,bast_valuedate,bast_lasttransmission)
into statements_part_array
from bast_bankstatement
inner join baac_bankaccount
on bast_baac_id = baac_bankaccount.baac_id
where baac_folder_id = folderid
and (bast_valuedate between date1 and date2)
and bast_state = 1)
as statements_part_array;

how to get the next date for a day of month

thanks for reading, this is the situation
I have a current_date and a day of month, so i need to know what will be the next date for this day of month, having in mind that some month don't have 30 and 31.
Example:
current_date = '2018-09-24'
day_of_week = 31
Expected result: '2018-12-31'
Currently i have this:
create or replace function next_diff(vals int[], current_val int) returns int as
$$
declare v int;
declare o int := vals[1];
begin
foreach v in array vals loop
if current_val >= o and current_val < v then
return v - current_val;
end if;
o := v;
end loop;
return vals[1] - current_val;
end;
$$ language plpgsql;
and this:
create or replace function next_day_of_month(days_of_month int[], curr_date date) returns date as
$$
declare cur_dom int := extract(day from curr_date);
declare next_diff int := next_diff(days_of_month, cur_dom);
begin
if next_diff < 0 then
curr_date := curr_date + '1 months'::interval;
end if;
curr_date := curr_date + (next_diff || 'days')::interval;
return curr_date;
end;
$$ language plpgsql;
but for this calling:
select next_day_of_month(array[31], '2018-09-24');
i am getting:
"2018-10-01"
Extra example
If i have this value
current_date = '2018-02-01'
day_of_week = 31
i will need the next month with 31th but i can't get '2018-02-31' because February don't have 31th then i should get '2018-02-31' because March have 31th.
Conclusion
if the month don't have the specified day must ignore the month and jump to the next.
thanks for all
Final method
Using Carlos Gomez answer, i create this PostgreSQL function and work perfectly:
create or replace function next_day_date(curr_date date, day_of_month int) returns date as
$$
declare next_day date;
begin
SELECT next_day_date into next_day FROM (
SELECT make_date_nullable(EXTRACT(year from n.month)::int, EXTRACT(month from n.month)::int, day_of_month) AS next_day_date
FROM (
SELECT generate_series(curr_date, curr_date + '3 months'::interval, '1 month'::interval) as month
) n
) results
WHERE results.next_day_date IS NOT NULL and results.next_day_date > curr_date LIMIT 1;
return next_day;
end;
$$ language plpgsql;
just add other filter in where clause and results.next_day_date > curr_date to prevent get the same or previous values for specified date
Thanks everyone for helping
Thenks Carlos you are the best
Gracias carlos eres el mejor :)
Your examples don't really match up but I think I know what you are trying to solve for (your first example result should be '2018-10-31' since October has 31 days and your second example result should be '2018-03-31'). It seems that given a date and a day of month you want to find the next month that has that day of month. To do this, I would do the following:
This function just wraps make_date to let it return null since it throws an exception if a date given to it is out of bounds (like February 30).
CREATE OR REPLACE FUNCTION make_date_nullable(year int, month int, day int)
RETURNS date as $$
BEGIN
RETURN make_date(year, month, day);
EXCEPTION WHEN others THEN RETURN null;
END;
$$ language plpgsql;
This SELECT first generates the next three months starting with the current one, then makes date out of them with your provided day_of_month and finally gets the first one that isn't null (exists according to postgresql.
SELECT next_day_date FROM (
SELECT make_date_nullable(EXTRACT(year from n.month)::int, EXTRACT(month from n.month)::int, day_of_month) AS next_day_date
FROM (
SELECT generate_series(current_date, current_date + '3 months'::interval, '1 month'::interval) as month
) n
) results
WHERE results.next_day_date IS NOT NULL LIMIT 1;
Hope this helps!

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;

Syntax error at or near ","

I have problems with this function and couldn't figure out how to fix it.
Create Function Quy(sdate timestamp)
returns integer as $$
declare
numbmonth integer;
quy integer;
Begin
numbmonth := Date_part('month',sdate);
If numbmonth < 4 then
quy := 1;
else if numbmonth < 7 then
quy := 2;
else if numbmonth < 10 then
quy := 3;
else quy := 4;
return quy;
END;
$$
LANGUAGE plpgsql;
This happens when I try to run the code:
ERROR: syntax error at or near ";"
LINE 16: END;
I really don't understand what is wrong with this.
Multiple syntax errors. The function would work like this:
CREATE OR REPLACE FUNCTION quy(sdate timestamp)
RETURNS integer AS
$func$
DECLARE
numbmonth integer := date_part('month', sdate);
quy integer;
BEGIN
IF numbmonth < 4 THEN
quy := 1;
ELSIF numbmonth < 7 THEN
quy := 2;
ELSIF numbmonth < 10 THEN
quy := 3;
ELSE
quy := 4;
END IF;
RETURN quy;
END
$func$ LANGUAGE plpgsql;
Consult the manual for the basic syntax of IF.
But that's much ado about nothing. To get the quarter of the year use the field specifier QUARTER with date_part() or EXTRACT() in a simple expression:
EXTRACT(QUARTER FROM $timestamp)
EXTRACT is the standard SQL equivalent of date_part().
Either returns double precision, so cast to integer if you need that (::int).
If you still need a function:
CREATE OR REPLACE FUNCTION quy(sdate timestamp)
RETURNS int LANGUAGE sql IMMUTABLE AS
'SELECT EXTRACT(QUARTER FROM $1)::int';
$1 is the reference to the 1st function parameter. Equivalent to sdate in the example. $-notation works in any version of Postgres, while named parameter references in SQL functions were only introduced with Postgres 9.2. See:
PLPGSQL Function to Calculate Bearing
dbfiddle here

get rank of players' height in plpgsql

We are going to have the rank of height, but I got 0 for all players
I convert the feet and inches into cm first, and use the sample code teacher gave us.
Here is my code:
CREATE OR REPLACE FUNCTION player_height_rank (firstname VARCHAR, lastname VARCHAR) RETURNS int AS $$
DECLARE
rank INTEGER:= 0;
offset INTEGER:= 0;
tempValue FLOAT:= 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
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.firstname = $1 AND r.lastname = $2 THEN
RETURN rank;
END IF;
END LOOP;
-- not in DB
RETURN 0;
END;
$$ LANGUAGE plpgsql;
--select * from player_height_rank('Ming', 'Yao');
Your function works fine for me if I correct two bugs:
One of your commas is not really a comma, but a “fullwidth comma”, UNICODE code point FF0C, which causes a syntax error.
You have a variable name offset, which causes SQL syntax errors because it is a reserved key word in SQL. If you really need to use that name, you have to enclose it in double quotes (") throughout, but it is better to choose a different name.
The reason this causes a problem is that an assignment like offset := offset + 1; in PL/pgSQL is translated into an SQL statement like SELECT offset + 1 INTO offset;.
You can do the whole thing in a single SQL query, which is more efficient:
SELECT rank
FROM (SELECT firstname,
lastname,
rank() OVER (ORDER BY h_feet + 12 * h_inches)
FROM players
) dummy
WHERE firstname = 'Ming'
AND lastname = 'Yao';