postgresql: Group select by year and month from epoch time - postgresql

I need to get an output from the postgresql db as follows:
2016
Nov
Dec
2017
Jan
Feb
Mar
I've been googling, testing, playing but hasn't come close.
So I tuen to you guys that will show me what nord I am!
I do know how to do this in mysql but postgres no.
Oh yeah it is Postgres 9.6.

Not sure, but may be you need this ?
with your_table(dt) as(
select '2016-11-01'::date union all
select '2016-11-02'::date union all
select '2016-12-01'::date union all
select '2017-01-01'::date union all
select '2017-02-01'::date union all
select '2017-02-02'::date union all
select '2017-03-01'::date union all
select '2017-10-01'::date union all
select '2017-11-01'::date union all
select '2018-02-01'::date
)
-- Above is just table simulation, actual query is below:
select case when month_name is null then y::text else month_name end from (
select extract(year from dt) as y, 0 as month_number, null as month_name from your_table group by extract(year from dt)
union all
select extract(year from dt), extract(month from dt) as month_number, '--'||to_char(min(dt), 'Mon') as month_name from your_table
group by extract(year from dt), extract(month from dt)
) t
order by y, month_number

Related

How to get value previous month & week?

Right now I'm getting an average for each month
SELECT EXTRACT(MONTH FROM date_time) AS month,
EXTRACT(YEAR FROM date_time) AS year,
avg("total")
FROM my_table
GROUP BY EXTRACT(MONTH FROM date_time), EXTRACT(YEAR FROM date_time)
But the SQL query needs to adjust so the total value current month - previous month
Is it possible?
For weekly
SELECT EXTRACT(WEEK FROM date_time) AS week,
EXTRACT(YEAR FROM date_time) AS year,
avg("total")
FROM my_table
GROUP BY EXTRACT(WEEK FROM date_time), EXTRACT(YEAR FROM date_time)
Yes, it's possible:
SELECT t1.month, t2.year, t1.tot - t2.tot FROM
(
SELECT EXTRACT(MONTH FROM date_time) AS month, EXTRACT(YEAR FROM date_time) AS year, avg("total") AS tot
FROM my_table GROUP BY EXTRACT(MONTH FROM date_time), EXTRACT(YEAR FROM date_time)
) t1
join (
SELECT EXTRACT(MONTH FROM date_time) AS month, EXTRACT(YEAR FROM date_time) AS year, avg("total") AS tot
FROM my_table GROUP BY EXTRACT(MONTH FROM date_time), EXTRACT(YEAR FROM date_time)
) t2
on ((t1.year = t2.year) and (t1.month = t2.month + 1)) or
((t1.year = t2.year + 1) and (t1.month = 1) and (t2.month = 12))
I have taken your select and converted it into two subselects, named them as t1 and t2 respectively and joined them by the criteria of left join.
Note that the very first month will not have a pair currently and if you need it nevertheless, then you can use left join and coalesce to make sure that even an unpaired item has a "pair" and a NULL for tot is defaulted to 0.
Note further that you can convert this subquery to a view for better readability.
If I get that correctly, you can first group avg(total) by yer and month, and the use LAG() window function to get previous month value, something like:
with my_table(date_time, total) as (
values
('2022-03-29', 10),
('2022-04-29', 12),
('2022-05-30', 20),
('2022-05-31', 30)
)
,grouped as (
SELECT EXTRACT('MONTH' FROM date_time::timestamp) AS month, EXTRACT('YEAR' FROM date_time::timestamp) AS year, avg("total") AS total
FROM my_table
GROUP BY EXTRACT('MONTH' FROM date_time::timestamp) , EXTRACT('YEAR' FROM date_time::timestamp)
)
SELECT *, LAG(total) OVER(ORDER BY year, month) as prev_month_total
FROM grouped

PostgreSQL (WITH RECURSIVE)

I have a query that returns all dates between 01/01/2011 and 12/31/2041 (excluding Saturdays and sundays). It's working fine, but when I try to put the filter "TRIM(TO_CHAR(dt, 'DAY')) NOT IN ('SATURDAY', 'SUNDAY')" in the WITH, It only returns one row. Any suggestions? Please keep in mind that i'm brand new to PostgreSQL.
WITH RECURSIVE bd(dt,rn) AS
(
SELECT
TO_DATE('01/01/2011', 'MM/DD/YYYY') AS dt,
1 AS rn
UNION
SELECT
dt + 1, rn + 1
FROM
bd
WHERE
dt BETWEEN TO_DATE('01/01/2011', 'MM/DD/YYYY') AND TO_DATE('12/31/2041', 'MM/DD/YYYY') - 1
)
SELECT
dt, rn
FROM
bd
WHERE TRIM(TO_CHAR(dt, 'DAY')) NOT IN ('SATURDAY', 'SUNDAY')
No need for a recursive query. This can be done much easier using generate_series()
select t.dt::date
from generate_series(date '2011-01-01', date '2014-12-31', interval '1 day') as t(dt)
where extract(isodow from t.dt) not in (6,7);

SQL Group by Month

I have a table of Sales data with each line as a sale date mm/dd/yy.
I'm trying to create a query so I can see total sales for each month I have.
Would I have to create a column separate that dictates only the month? Or is there a way that it can take the month from that date format?
The short answer is: You don't need a separate column. You can group by the result of a function call.
The details of what that function might depend on your database, how you want results formatted, and performance considerations.
The following both work in Oracle:
SELECT extract(YEAR FROM ae.saledate), extract(MONTH FROM ae.saledate), count(*)
FROM mytable ae
GROUP BY extract(YEAR FROM ae.saledate), extract(MONTH FROM ae.saledate);
SELECT TO_CHAR(ae.saledate, 'YYYY-MM'), count(*)
FROM mytable ae
GROUP BY TO_CHAR(ae.saledate, 'YYYY-MM');
Edited to add versions that ignore year and only look at month (since I was making an assumption above that wasn't actually in the question):
SELECT extract(MONTH FROM ae.saledate), count(*)
FROM mytable ae
GROUP BY extract(MONTH FROM ae.saledate);
SELECT TO_CHAR(ae.saledate, 'MM'), count(*)
FROM mytable ae
GROUP BY TO_CHAR(ae.saledate, 'MM');
The following Query will helpful.
CREATE TABLE #TEMP
(SalesDate DATETime,
Amount float
)
INSERT INTO #TEMP
SELECT '2016-01-12', 12
UNION
SELECT '2016-01-13', 12
UNION
SELECT '2016-02-12', 12
UNION
SELECT '2016-03-12', 12
SELECT CONVERT(VARCHAR(7), SalesDate, 120) AS 'YYYY-MM',
SUM(Amount) as 'Amount'
FROM #Temp
GROUP BY CONVERT(VARCHAR(7), SalesDate, 120)
OUTPUT :
YYYY-MM Amount
------- ----------------------
2016-01 24
2016-02 12
2016-03 12
(3 row(s) affected)
For only Month Wise
SELECT RIGHT(CONVERT(VARCHAR(7), SalesDate, 120), 2) AS 'MM',
sum(Amount) as 'Amount'
from #Temp
group by RIGHT(CONVERT(VARCHAR(7), SalesDate, 120), 2)
Output:
MM Amount
---- ----------------------
01 24
02 12
03 12
(3 row(s) affected)

Last Working Day is showing null while on weekend

Here is my code but its showing null while today is friday. But I would like to get last working day.
-- Insert statements for procedure here
--Below is the param you would pass
DECLARE #dateToEvaluate date=GETDATE();
--Routine
DECLARE #startDate date=CAST('1/1/'+CAST(YEAR(#dateToEvaluate) AS char(4)) AS date); -- let's get the first of the year
WITH
tally(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1 FROM sys.all_columns),
dates AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS dt_id,
DATEADD(DAY,n,#startDate) AS dt,
DATENAME(WEEKDAY,DATEADD(DAY,n,#startdate)) AS dt_name
FROM tally
WHERE n<366 --arbitrary
AND DATEPART(WEEKDAY,DATEADD(DAY,n,#startDate)) NOT IN (6)
AND DATEADD(DAY,n,#startDate) NOT IN (SELECT CAST(HolidayDate AS date) FROM Holiday)),
curr_id(id) AS (SELECT dt_id FROM dates WHERE dt=#dateToEvaluate)
SELECT d.dt
FROM dates AS d
CROSS JOIN
curr_id c
WHERE d.dt_id+1=c.id
The code below will take any date and "walk backward" to find the previous week day (M-F) which is not in the #holidays table.
declare #currentdate datetime = '2015-03-22'
declare #holidays table (holiday datetime)
insert #holidays values ('2015-03-20')
;with cte as (
select
#currentdate k
union all
select
dateadd(day, -1, k)
from cte
where
k = #currentdate
or ((datepart(dw, k) + ##DATEFIRST - 1 - 1) % 7) + 1 > 5 --determine day of week independent of culture
or k in (select holiday from #holidays)
)
select min(k) from cte
The dates table doesn't have any FRIDAY dates in it. Change the NOT IN (6) to NOT IN (1, 7). This will remove Saturday and Sundays from the dates table.

Getting the start date and end date of all weeks by giving the year

I have a query which displays a set of 52 numbers along with the respective dates of that week
SELECT kkk, TO_CHAR (start_date, 'DD-MON-YYYY'),
TO_CHAR (start_date + 6, 'DD-MON-YYYY') AS end_day
FROM (SELECT TRUNC (TRUNC (TO_DATE ('2014', 'YYYY'), 'YYYY') + 1 * 7,
'IW'
)
- 1 start_date,
ROWNUM AS kkk
FROM DUAL
CONNECT BY ROWNUM <= 52);
but the problem with this query is that I am only getting the first week dates but not for the next consecutive weeks.Please help
Try like this,
SELECT kkk,
TO_CHAR(start_date, 'DD-MON-YYYY') start_date,
TO_CHAR(start_date + 6, 'DD-MON-YYYY') end_day
FROM(
SELECT TRUNC(Trunc(to_date('2014', 'YYYY'),'YYYY')+ LEVEL * 7,'IW')-1 start_date ,
ROWNUM kkk
FROM duaL
CONNECT BY LEVEL <= 52
);
Instead of ROWNUM you can also use LEVEL, Like,
SELECT TRUNC(Trunc(to_date('2014', 'YYYY'),'YYYY')+ LEVEL * 7,'IW')-1 start_date ,
level kkk
FROM duaL
CONNECT BY LEVEL <= 52