I have two date parameters BeginDate and EndDate. I need to compare these two dates and then perform some sql select. For example, if BeginDate is lessthan or equal to EndDate, then only I need to query data. For example as below:
If #BeginDate <= #EndDate Then
select statement1
select statement2
...
End if
When I tried in DB2, I am getting an error.
Kindly suggest a sample with DB2 syntax.
In case you want to do it simple, just add the condition to both select statements:
SELECT *
FROM <table_name>
WHERE #BeginDate <= #EndDate;
Other solution would be using IF statement within PL/SQL contexts to execute SQL:
IF (#BeginDate <= #EndDate) THEN
statements
END IF;
A more complete example for inline SQL PL, would be something like:
CREATE PROCEDURE EXAMPLE_IF
(IN BeginDate DATE, IN EndDate DATE)
LANGUAGE SQL
BEGIN ATOMIC
IF (BeginDate <= EndDate)
THEN
statement1;
statement2;
END IF;
END!
Hope it's useful!
Related
I am trying to do something similar to below MSSQL code in postgresql but i am getting error.
MSSQL code
IF (YEAR(GETDATE()) ='2020')
SELECT 1
ELSE SELECT 2
Postgres CODE
CREATE PROCEDURE PROCNAME(
DATEVAR timestamp
)
AS $BODY$
DECLARE
BEGIN
if (Extract (YEAR FROM TIMESTAMP DATEVAR) ='2020') THEN
DO SOMETHING;
ELSE DO SOMETHING ELSE;
END IF ;
$BODY$;
ERROR: syntax error at or near "DATEVAR"
Remove the timestamp prefix it is only required if you want to refer to a constant.
And numbers should be compared to numbers, not to strings.
CREATE PROCEDURE PROCNAME(
DATEVAR timestamp
)
AS $BODY$
DECLARE
BEGIN
if Extract (YEAR FROM DATEVAR) = 2020 THEN
DO SOMETHING;
ELSE DO SOMETHING ELSE;
END IF;
$BODY$;
I want to store the result of fetch into a variable how we can do that in postgresql?
I also tried by creating a function it isn't working
code :
begin work;
DECLARE
cur_films CURSOR
FOR select CURRENT_DATE + i date_
FROM generate_series(date '2019-11-11'- CURRENT_DATE, date '2019-11-15' - CURRENT_DATE ) i;
fetch forward 2 from cur_films;
close cur_films;
commit work;
If you want to pass all values generated by your query to a function or procedure, you can aggregate everything into an array, then pass that array to the function:
DECLARE
l_dates date[];
begin
select array_agg(g.dt::date)
into l_dates
from generate_series(date '2019-11-11', date '2019-11-15', interval '1 day') as g(dt);
perform your_function(l_dates);
end;
But you wouldn't need PL/pgSQL for that. This can also be done in plain SQL:
with input as (
select array_agg(g.dt::date) as dates
from generate_series(date '2019-11-11', date '2019-11-15', interval '1 day') as g(dt)
)
select *
from your_function((select dates from input));
I'm using PostgreSQL 9.6 with a query that roughly looks like this:
DO $$
DECLARE max_sales_date DATE ;
BEGIN
max_sales_date :=
select sales_date::date
from (
select count(sales_date::date)
, sales_date::date
, row_number() over (order by count(sales_date::date) desc) as rn
from Sales
group by sales_date::date
) a where a.rn = 1 ;
select *
from Sales
where sales_date = max_sales_date ;
END $$ ;
As you can see, I want to get the day with the highest number of sales, store it in a variable and use it in another query. I am aware that a variable is not necessary in that case but I need this for another step whose development has not started yet.
DBeaver 5.0 unfortunately throws me the following error message:
Internal jdbc driver error
java.lang.ArrayIndexOutofBoundsExcception:
I also have to mention that the query whose result is stored in the variable works fine when used independently.
I therefore have two questions:
Why doesn't the whole code work?
How can I reach the result I want?
EDIT: the below comments made me realize I should clarify my intentions: The reason why I'd like to keep using a variable is because I was planning to perform some simple computations on it afterwards (e.g. add one day, create a loop, etc.). If you guys can find an easy solution for this without using a variable, I'd be glad too :)
You can use select ... into pattern, and you can't have a "select *" query in a DO function - it is not meant to return a query.
DO $$
DECLARE max_sales_date DATE ;
declare sale record;
BEGIN
select sales_date into max_sales_date from
(select sales_date from (select count(sales_date) as cnt, sales_date::date
from Sales group by sales_date) as dt order by cnt desc limit 1) as ct;
for sale in select * from Sales where sales_date = max_sales_date
loop
-- do whatever is needed with this data
end loop;
END $$ ;
I want to create a function to get the right week number of year.
I already posted here to find a 'native' solution, but apparently there is not.
I tryed to create funcrtion based on this mysql example
Here is the code translated to postgresql:
CREATE OR REPLACE FUNCTION week_num_year(_date date)
RETURNS integer AS
$BODY$declare
_year integer;
begin
select date_part('year',_date) into _year;
return ceil((to_char(_date,'DDD')::integer+(to_char(('01-01-'||_year)::date,'D')::integer%7-7))/7);
end;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
But it gives wrong result, can someone help me ?
My config: PostgreSQL 9.2
If you want proper week numbers use:
select extract(week from '2012-01-01'::date);
This will produce the result 52, which is correct if you look on a calendar.
Now, if you actually want to define week numbers as "Every 7 days starting with the first day of the year" that's fine, though it doesn't match the week numbers anyone else uses and has some odd quirks:
select floor((extract(doy from '2011-01-01'::date)-1)/7)+1;
By the way, parsing date strings and hacking them up with string functions is almost always a really bad idea.
create or replace function week_num_year(_date date)
returns integer as
$body$
declare
_year date;
_week_number integer;
begin
select date_trunc('year', _date)::date into _year
;
with first_friday as (
select extract(doy from a::date) ff
from generate_series(_year, _year + 6, '1 day') s(a)
where extract(dow from a) = 5
)
select floor(
(extract(doy from _date) - (select ff from first_friday) - 1) / 7
) + 2 into _week_number
;
return _week_number
;
end;
$body$
language plpgsql immutable
You can retrieve the day of the week and also the week of the year by running:
select id,extract(DOW from test_date),extract(week from test_date), testdate,name from yourtable
What about the inbuild extract function?
SELECT extract (week from current_timestamp) FROM A_TABLE_FROM_YOUR_DB;
I need to check the previous record's element to make sure the date I query doesn't fall within a specific range between ending date and 7 days before starting date. I have the following code:
create or replace function eight (date) returns text as $$
declare
r record;
checkDate alias for $1;
begin
for r in
select * from periods
order by startDate
loop
if (checkDate between r.startDate and r.endDate) then
return q3(r.id);
elsif (checkDate between (r.startDate - interval '7 days') and r.startDate) then
return q3(r.id);
elsif (checkDate between (lag(r.endDate) over (order by r.startDate)) and (r.startDate - interval '8 days')) then
return q3(r.id);
end if;
end loop;
return null;
end;
$$ language plpgsql;
So basically, I need to check for the following:
If the query date is between the starting and ending dates
If the query date is 7 days before the start of the starting date
If the query date is between ending date and the starting date
and return the id that is associated with that date.
My function seems to work fine in most cases, but there are cases that seem to give me 0 results (when there should always be 1 result) is there something missing in my function? I'm iffy about the last if statement. That is, trying to check from previous records ending date to current records starting date (with the 7 day gap)
EDIT: no dates overlap.
Edit: Removed the part about RETURN NEXT - I had misread the question there.
Doesn't work the way you have it. A window function cannot be called like that. Your record variable r is like a built-in cursor in a FOR loop. Only the current row of the result is visible inside the loop. You would have to integrate the window function lag() it into the initial SELECT.
But since you are looping through the rows in a matching order anyway, you can do it another way.
Consider this largely rewritten example. Returns at the first violating row:
CREATE OR REPLACE FUNCTION q8(_day date)
RETURNS text AS
$BODY$
DECLARE
r record;
last_enddate date;
BEGIN
FOR r IN
SELECT *
-- ,lag(r.endDate) OVER (ORDER BY startDate) AS last_enddate
-- commented, because I supply an alternative solution
FROM periods
ORDER BY startDate
LOOP
IF _day BETWEEN r.startDate AND r.endDate THEN
RETURN 'Violates condition 1'; -- I return differing results
ELSIF _day BETWEEN (r.startDate - 7) AND r.startDate THEN
RETURN 'Violates condition 2';
ELSIF _day BETWEEN last_enddate AND (r.startDate) THEN
-- removed "- 7 ", that is covered above
RETURN 'Violates condition 3';
END IF;
last_enddate := r.enddate; -- remember for next iteration
END LOOP;
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
More hints
Why the alias for $1? You named it _day in the declaration already. Stick to it.
Be sure to know how PostgreSQL handles case in identifiers. ( I only use lower case.)
You can just add / subtract integers (for days) from a date.
Are you sure that lag() will return you something? I'm pretty sure that this is out of context here. Given that rows from periods are selected in order, you can store the current startDate in a variable, and use it in the if statement of the next cycle.
SET search_path='tmp';
DROP table period;
CREATE table period
( start_date DATE NOT NULL
, end_date DATE
);
INSERT INTO period(start_date ,end_date) VALUES
( '2012-01-01' , '2012-02-01' )
, ( '2012-02-01' , '2012-02-07' )
, ( '2012-03-01' , '2012-03-15' )
, ( '2012-04-01' , NULL )
, ( '2012-04-17' , '2012-04-21' )
;
DROP FUNCTION valid_date(DATE) ;
CREATE FUNCTION valid_date(DATE) RETURNS boolean
AS $body$
declare
found boolean ;
zdate ALIAS FOR $1;
begin
found = false;
SELECT true INTO found
WHERE EXISTS (
SELECT * FROM period p
WHERE (p.start_date > zdate
AND p.start_date < zdate + interval '7 day' )
OR ( p.start_date < zdate AND p.end_date > zdate )
OR ( p.start_date < zdate AND p.end_date IS NULL
AND p.start_date >= zdate - interval '7 day' )
)
;
if (found = true) then
return false;
else
return true;
end if;
end;
$body$ LANGUAGE plpgsql;
\echo 2011-01-01:true
SELECT valid_date('2011-01-01' );
\echo 2012-04-08:false
SELECT valid_date('2012-04-08' );
\echo 2012-04-30:true
SELECT valid_date('2012-04-30' );
BTW: I really think that the required functionality should be implemented as a table constraint, imposed by a trigger function (that might be based on the above function).