how to get month only from current year - postgresql

I am working with postgresql
I am trying to get every month from current year
see postgres date image
current all month are get see below script and image
select extract(month from orderdate)
FROM public.orderalbum;
see my code in c# side
//month wise sale
[HttpGet]
public ActionResult getmonthSales()
{
NpgsqlCommand cmd = new NpgsqlCommand("SELECT COUNT(albumid) as albumid, getmonthfromdate(orderdate) as y FROM public.orderAlbum GROUP BY getmonthfromdate(orderdate)", conn);
please see my above select query I pass a function in select query
I want to show every date from current year
create or replace function getyearfromdate(year date)
returns table (orderdate int)
as
$$
begin
return QUERY
select extract(year from orderdate)::int
FROM public.orderalbum;
end;
$$
language plpgsql;
I want to show every month from current year

SELECT
extract(month FROM t.dt) AS yr_month,
extract(year from t.dt) as yr
FROM (
VALUES ('2022-12-14'::date),
('2022-01-30'::date),
('2021-06-14'::date)) AS t (dt)
WHERE
extract(year FROM t.dt) = 2022;
yr_month | yr
----------+------
12 | 2022
1 | 2022

Related

DATE ADD function in PostgreSQL

I currently have the following code in Microsoft SQL Server to get users that viewed on two days in a row.
WITH uservideoviewvideo (date, user_id) AS (
SELECT DISTINCT date, user_id
FROM clickstream_videos
WHERE event_name ='video_play'
and user_id IS NOT NULL
)
SELECT currentday.date AS date,
COUNT(currentday.user_id) AS users_view_videos,
COUNT(nextday.user_id) AS users_view_next_day
FROM userviewvideo currentday
LEFT JOIN userviewvideo nextday
ON currentday.user_id = nextday.user_id AND DATEADD(DAY, 1,
currentday.date) = nextday.date
GROUP BY currentday.date
I am trying to get the DATEADD function to work in PostgreSQL but I've been unable to figure out how to get this to work. Any suggestions?
I don't think PostgreSQL really has a DATEADD function. Instead, just do:
+ INTERVAL '1 day'
SQL Server:
Add 1 day to the current date November 21, 2012
SELECT DATEADD(day, 1, GETDATE()); # 2012-11-22 17:22:01.423
PostgreSQL:
Add 1 day to the current date November 21, 2012
SELECT CURRENT_DATE + INTERVAL '1 day'; # 2012-11-22 17:22:01
SELECT CURRENT_DATE + 1; # 2012-11-22 17:22:01
http://www.sqlines.com/postgresql/how-to/dateadd
EDIT:
It might be useful if you're using a dynamic length of time to create a string and then cast it as an interval like:
+ (col_days || ' days')::interval
You can use date + 1 to do the equivalent of dateadd(), but I do not think that your query does what you want to do.
You should use window functions, instead:
with plays as (
select distinct date, user_id
from clickstream_videos
where event_name = 'video_play'
and user_id is not null
), nextdaywatch as (
select date, user_id,
case
when lead(date) over (partition by user_id
order by date) = date + 1 then 1
else 0
end as user_view_next_day
from plays
)
select date,
count(*) as users_view_videos,
sum(user_view_next_day) as users_view_next_day
from nextdaywatch
group by date
order by date;

Exporting text file from PostgreSQL in slices (year wise distribution of text files)

I have a query which exports results to a text file. The code is below:
Copy (
Select
foo.gid As addr_ID,
bar.geom As streets,
St_LineInterpolatePoint
(ST_LineMerge(bar.geom),
St_LineLocatePoint
(St_LineMerge(bar.geom),foo.geom)
) As interpolated_point
FROM foo
Left JOIN bar ON ST_DWithin(foo.geom, bar.geom, 50)
ORDER BY
foo.gid, St_Distance(foo.geom, bar.geom)
)
To '~path/my_file.txt' WITH DELIMITER ',';
Table 'foo' contains 87 rows and 'bar' contains 16,060 rows. Table 'foo' also contains two columns 'start_time' and 'end_time' depicting an interval of varying years like this:
Start_time End_time
2003-06-07 00:00:00 2004-09-30 23:59:59
2004-02-03 00:00:00 2005-03-10 23:59:59
2003-07-09 00:00:00 2012-05-06 23:59:59
I would like to export my select query results so that it generates TEXT files for each year like this:
Expected Output:
results_2003.txt
results_2004.txt
results_2005.txt
and so on...
Can someone suggest me how to do that? I am using PostgreSQL version 9.5 (x64) on Windows 7 Enterprise (x64).
EDIT (1):
The text files need to be exported for each year for example: In 'start_time' and 'end_time' columns, if lower bound is 2003 and upper bound is 2012 then file for each year needs to be exported as shown in expected output.
EDIT (2):
So far I have been trying to develop the full working code (many thanks to #Phillip for code and suggested links) but I am running into problems as I am repeatedly getting a syntax error. The error says "syntax error at or near "1" (Where clause). Below I have posted code (again CREDIT goes to Phillip):
-- In the below code: Addr, streets, agfisotime (start time in data type timestamp) and agtisotime (end time in data type timestamp) are original columns
DO $$
DECLARE
start_year date;
end_year date;
current_year date;
file_name text;
BEGIN
select date_trunc('year', min(agfisotime)), date_trunc('year', max(agtisotime))
into start_year, end_year
from Addr;
FOR current_year IN
SELECT * FROM generate_series(start_year, end_year, interval '1 year')
LOOP
Execute format('
Copy(
Select
Addr.gid As addr_ID,
streets.geom As streets,
St_LineInterpolatePoint(ST_LineMerge(streets.geom),
St_LineLocatePoint(St_LineMerge(streets.geom),Addr.geom))
As interpolated_point
FROM Addr
Left JOIN streets ON ST_DWithin(Addr.geom, streets.geom, 50)
WHERE agfisotime < current_year + interval '1 year'
AND agtisotime >= current_year
ORDER BY
Addr.gid, St_Distance(Addr.geom, streets.geom)
) TO ''C:/temp/out.csv'';
',current_year);
END LOOP;
RETURN;
END
$$
This is quite complicated but it is possible. You basic problem is that you need to fire the COPY command once for every file you want to write, and you need to do so using dynamic SQL. There's an explanation of that here: Use function variable in dynamic COPY statement
To actually fire the COPY command multiple times you need to use an anonymous code block or a function to retrieve a list of years and then export by firing the COPY command for each year.
I've tested the following and it show's the basic principle although it's not exporting the correct data:
DO $$
DECLARE
start_year date;
end_year date;
current_year date;
file_name text;
BEGIN
select date_trunc('year', min(start_time)), date_trunc('year', max(end_time))
into start_year, end_year
from foo;
FOR current_year IN
SELECT * FROM generate_series(start_year, end_year, interval '1 year')
LOOP
file_name := '/tmp/myFile_' || extract(year from current_year) || '.csv';
--- do your export here using current_year to filter the results
EXECUTE format('
COPY (
select start_time, end_time from foo
WHERE start_time < %L::date + interval ''1 year''
AND end_time >= %L::date
)
TO %L WITH DELIMITER '','';
',
current_year, current_year, file_name);
END LOOP;
END
$$;
For each year you will need to filter the results. So your query will look something like this (untested):
EXECUTE format('
COPY (
Select
foo.gid As addr_ID,
bar.geom As streets,
St_LineInterpolatePoint
(ST_LineMerge(bar.geom),
St_LineLocatePoint
(St_LineMerge(bar.geom),foo.geom)
) As interpolated_point
FROM foo
Left JOIN bar ON ST_DWithin(foo.geom, bar.geom, 50)
WHERE start_time < %L::date + interval ''1 year''
AND end_time >= %L::date
ORDER BY
foo.gid, St_Distance(foo.geom, bar.geom)
)
TO %L WITH DELIMITER '','';
',
current_year, current_year, file_name);

how to calculate WeekOfMonth in SQL

Trying to calculate holidays in a given year for loading DimDate table. There is an option in SQL to calculate the WeekOfYear Number but couldnt find a function to calculate the WeekNumberOfMonth.
How to find week number of a given month to calculate holidays in a year to fill DIMDate table
Logic
CurrentDateWeek - CurrentDateMonthBeginWeek
the difference between the week Number for a given date and the first week number for the given date provides week of a month
Simple One Line SQL Statement
Declare #CurrentDate as datetime
set #CurrentDate = '5/31/2014'
Select (Datepart(week,#CurrentDate) - (Datepart(week,cast(month(#CurrentDate) as varchar) + '/1/' + cast(Year(#CurrentDate) as varchar))) + 1) as WeekOfMonth
Stored Procedure
Declare #CurrentDate as datetime
Declare #BeginWeek as int
Declare #EndWeek as Int
set #CurrentDate = '5/31/2014'
SET #EndWeek = DATEPART(week,eomonth(#CurrentDate))
SET #BeginWeek = Datepart(week,cast(month(#CurrentDate) as varchar) + '/1/' + cast(Year(#CurrentDate) as varchar))
Select Datepart(week,#CurrentDate) - #BeginWeek + 1
Select #BeginWeek as beginweek
select #EndWeek as endweek
Select Datepart(week,#CurrentDate)
Using the above logic we could fill the DIMDATE table with holiday days

PostgreSql - Adding YEar and Month to Table

I am creating a Customer table and i want one of the attributes to be Expiry Date of credit card.I want the format to be 'Month Year'. What data type should i use? i want to use date but the format is year/month/day. Is there any other way to restrict format to only Month and year?
You can constrain the date to the first day of the month:
create table customer (
cc_expire date check (cc_expire = date_trunc('month', cc_expire))
);
Now this fails:
insert into customer (cc_expire) values ('2014-12-02');
ERROR: new row for relation "customer" violates check constraint "customer_cc_expire_check"
DETAIL: Failing row contains (2014-12-02).
And this works:
insert into customer (cc_expire) values ('2014-12-01');
INSERT 0 1
But it does not matter what day is entered. You will only check the month:
select
date_trunc('month', cc_expire) > current_date as valid
from customer;
valid
-------
t
Extract year and month separately:
select extract(year from cc_expire) "year", extract(month from cc_expire) "month"
from customer
;
year | month
------+-------
2014 | 12
Or concatenated:
select to_char(cc_expire, 'YYYYMM') "month"
from customer
;
month
--------
201412
Use either
char(5) for two-digit years, or
char(7) for four-digit years.
Code below assumes two-digit years, which is the form that matches all my credit cards. First, let's create a table of valid expiration dates.
create table valid_expiration_dates (
exp_date char(5) primary key
);
Now let's populate it. This code is just for 2013. You can easily adjust the range by changing the starting date (currently '2013-01-01'), and the "number" of months (currently 11, which lets you get all of 2013 by adding from 0 to 11 months to the starting date).
with all_months as (
select '2013-01-01'::date + (n || ' months')::interval months
from generate_series(0, 11) n
)
insert into valid_expiration_dates
select to_char(months, 'MM') || '/' || to_char(months, 'YY') exp_date
from all_months;
Now, in your data table, create a char(5) column, and set a foreign key reference from it to valid_expiration_dates.exp_date.
While you're busy with this, think hard about whether "exp_month" might be a better name for that column than "exp_date". (I think it would.)
As another idea you could essentially create some brief utilities to do this for you using int[]:
CREATE OR REPLACE FUNCTION exp_valid(int[]) returns bool LANGUAGE SQL IMMUTABLE as
$$
SELECT $1[1] <= 12 AND (select count(*) = 2 FROM unnest($1));
$$;
CREATE OR REPLACE FUNCTION first_invalid_day(int[]) RETURNS date LANGUAGE SQL IMMUTABLE AS
$$
SELECT (to_date($1[2]::text || $1[1]::text, CASE WHEN $1[2] < 100 THEN 'YYMM' ELSE 'YYYYMM' END) + '1 month'::interval)::date;
$$;
These work:
postgres=# select exp_valid('{04,13}');
exp_valid
-----------
t
(1 row)
postgres=# select exp_valid('{13,04}');
exp_valid
-----------
f
(1 row)
postgres=# select exp_valid('{04,13,12}');
exp_valid
-----------
f
(1 row)
Then we can convert these into a date:
postgres=# select first_invalid_day('{04,13}');
first_invalid_day
-------------------
2013-05-01
(1 row)
This use of arrays does not violate any normalization rules because the array as a whole represents a single value in its domain. We are storing two integers representing a single date. '{12,2}' is December of 2002, while '{2,12}' is Feb of 2012. Each represents a single value of the domain and is therefore perfectly atomic.

Count months between two timestamp on postgresql?

I want to count the number of months between two dates.
Doing :
SELECT TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP '2011-04-30 14:38:40';
Returns :
0 years 0 mons 409 days 20 hours 0 mins 0.00 secs
and so:
SELECT extract(month from TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP '2011-04-30 14:38:40');
returns 0.
age function returns interval:
age(timestamp1, timestamp2)
Then we try to extract year and month out of the interval and add them accordingly:
select extract(year from age(timestamp1, timestamp2)) * 12 +
extract(month from age(timestamp1, timestamp2))
Please note that the most voted answer by #ram and #angelin is not accurate when you are trying to get calendar month difference using.
select extract(year from age(timestamp1, timestamp2))*12 + extract(month from age(timestamp1, timestamp2))
for example, if you try to do:
select extract(year from age('2018-02-02'::date, '2018-03-01'::date))*12 + extract(month from age('2018-02-02'::date , '2018-03-01'::date))
the result will be 0 but in terms of months between March from February should be 1 no matter the days between dates.
so the formula should be like the following saying that we start with timestamp1 and timestamp2:
((year2 - year1)*12) - month1 + month2 = calendar months between two timestamps
in pg that would be translated to:
select ((extract('years' from '2018-03-01 00:00:00'::timestamp)::int - extract('years' from '2018-02-02 00:00:00'::timestamp)::int) * 12)
- extract('month' from '2018-02-02 00:00:00'::timestamp)::int + extract('month' from '2018-03-01 00:00:00'::timestamp)::int;
you can create a function like:
CREATE FUNCTION months_between (t_start timestamp, t_end timestamp)
RETURNS integer
AS $$
select ((extract('years' from $2)::int - extract('years' from $1)::int) * 12)
- extract('month' from $1)::int + extract('month' from $2)::int
$$
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
The age function give a justified interval to work with:
SELECT age(TIMESTAMP '2012-06-13 10:38:40', TIMESTAMP '2011-04-30 14:38:40');
returns 1 year 1 mon 12 days 20:00:00, and with that you can easily use EXTRACT to count the number of months:
SELECT EXTRACT(YEAR FROM age) * 12 + EXTRACT(MONTH FROM age) AS months_between
FROM age(TIMESTAMP '2012-06-13 10:38:40', TIMESTAMP '2011-04-30 14:38:40') AS t(age);
If you will do this multiple times, you could define the following function:
CREATE FUNCTION months_between (t_start timestamp, t_end timestamp)
RETURNS integer
AS $$
SELECT
(
12 * extract('years' from a.i) + extract('months' from a.i)
)::integer
from (
values (justify_interval($2 - $1))
) as a (i)
$$
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
so that you can then just
SELECT months_between('2015-01-01', now());
SELECT date_part ('year', f) * 12
+ date_part ('month', f)
FROM age ('2015-06-12', '2014-12-01') f
Result: 6 Months
Gives the differenece of months of two dates
SELECT ((extract( year FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract( year FROM TIMESTAMP '2011-04-30 14:38:40' )) *12) + extract(MONTH FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract(MONTH FROM TIMESTAMP '2011-04-30 14:38:40' );
The Result : 14
Have to extract months seperately for both the dates and then the difference of both the results
Here is a PostgreSQL function with the exact same behavior as the Oracle MONTHS_BETWEEN function.
It has been tested on a wide range of years (including leap ones) and more than 700k combinations of dates (including end of every months).
CREATE OR REPLACE FUNCTION months_between
( DATE,
DATE
)
RETURNS float
AS
$$
SELECT
(EXTRACT(YEAR FROM $1) - EXTRACT(YEAR FROM $2)) * 12
+ EXTRACT(MONTH FROM $1) - EXTRACT(MONTH FROM $2)
+ CASE
WHEN EXTRACT(DAY FROM $2) = EXTRACT(DAY FROM LAST_DAY($2))
AND EXTRACT(DAY FROM $1) = EXTRACT(DAY FROM LAST_DAY($1))
THEN
0
ELSE
(EXTRACT(DAY FROM $1) - EXTRACT(DAY FROM $2)) / 31
END
;
$$
LANGUAGE SQL
IMMUTABLE STRICT;
This function requires a LAST_DAY function (behaving the same as Oracle's one) :
CREATE OR REPLACE FUNCTION last_day
( DATE
)
RETURNS DATE
AS
$$
SELECT
(DATE_TRUNC('MONTH', $1) + INTERVAL '1 MONTH' - INTERVAL '1 DAY')::date
;
$$
LANGUAGE SQL
IMMUTABLE STRICT;
I had the same problem once upon a time and wrote this ... it's quite ugly:
postgres=> SELECT floor((extract(EPOCH FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract(EPOCH FROM TIMESTAMP '2005-04-30 14:38:40' ))/30.43/24/3600);
floor
-------
85
(1 row)
In this solution "one month" is defined to be 30.43 days long, so it may give some unexpected results over shorter timespans.
Extract by year and months will floor on months:
select extract(year from age('2016-11-30'::timestamp, '2015-10-15'::timestamp)); --> 1
select extract(month from age('2016-11-30'::timestamp, '2015-10-15'::timestamp)); --> 1
--> Total 13 months
This approach maintains fractions of months (thanks to tobixen for the divisor)
select round(('2016-11-30'::date - '2015-10-15'::date)::numeric /30.43, 1); --> 13.5 months
Try this solution:
SELECT extract (MONTH FROM age('2014-03-03 00:00:00'::timestamp,
'2013-02-03 00:00:00'::timestamp)) + 12 * extract (YEAR FROM age('2014-03-03
00:00:00'::timestamp, '2013-02-03 00:00:00'::timestamp)) as age_in_month;
SELECT floor(extract(days from TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP
'2011-04-30 14:38:40')/30.43)::integer as months;
Gives an approximate value but avoids duplication of timestamps. This uses hint from tobixen's answer to divide by 30.43 in place of 30 to be less incorrect for long timespans while computing months.
I made a function like this:
/* similar to ORACLE's MONTHS_BETWEEN */
CREATE OR REPLACE FUNCTION ORACLE_MONTHS_BETWEEN(date_from DATE, date_to DATE)
RETURNS REAL LANGUAGE plpgsql
AS
$$
DECLARE age INTERVAL;
declare rtn real;
BEGIN
age := age(date_from, date_to);
rtn := date_part('year', age) * 12 + date_part('month', age) + date_part('day', age)/31::real;
return rtn;
END;
$$;
Oracle Example)
SELECT MONTHS_BETWEEN
(TO_DATE('2015-02-02','YYYY-MM-DD'), TO_DATE('2014-12-01','YYYY-MM-DD') )
"Months" FROM DUAL;
--result is: 2.03225806451612903225806451612903225806
My PostgreSQL function example)
select ORACLE_MONTHS_BETWEEN('2015-02-02'::date, '2014-12-01'::date) Months;
-- result is: 2.032258
From the result you can use CEIL()/FLOOR() for rounding.
select ceil(2.032258) --3
select floor(2.032258) --2
Try;
select extract(month from age('2012-06-13 10:38:40'::timestamp, '2011-04-30 14:38:40'::timestamp)) as my_months;