SQL Code for counts over different time periods - sql

I need to figure out how to answer this:
Find the number of property views per branch within 1 month, 2 months, and 3 months of client registration in one query.
I'm struggling with how to put this in one query, would CASE be the best way?
Thanks for any input.

You could probably just use UNION
select '1 month' as type, count(*) from tablename where month < 1
union
select '2 month' as type, count(*) from tablename where month < 2
union
select '3 month' as type, count(*) from tablename where month < 3
This would yield your counts as 3 different rows.

Related

PostgreSQL - how to loop over a parameter to make union of different queries of the same table

I need to count the number of events still occurring in a given year, provided these events started before the given year and ended after it. So, the query I'm using now is just a pile of unparametrized queries and I'd like to write it out with a loop on a parameter:
select
count("starting_date" ) as "number_of_events" ,'2020' "year"
from public.table_of_events
where "starting_date" < '2020-01-01' and ("closing_date" > '2020-12-31')
union all
select
count("starting_date" ) as "number_of_events" ,'2019' "year"
from public.table_of_events
where "starting_date" < '2019-01-01' and ("closing_date" > '2019-12-31')
union all
select
count("starting_date" ) as "number_of_events" ,'2018' "year"
from public.table_of_events
where "starting_date" < '2018-01-01' and ("closing_date" > '2018-12-31')
...
...
...
and so on for N years
So, I have ONE table that must be filtered according to one parameter that is both part of the select statement and the where clause
The general aspect of the "atomic" query is then
select
count("starting_date" ) as "number_of_events" , **PARAMETER** "year"
from public.table_of_events
where "starting_date" < **PARAMETER** and ("closing_date" > **PARAMETER** )
union all
Can anyone help me put this in a more formal loop?
Thanks a lot, I hope I was clear enough.
You seem to want events that span entire years. Perhaps a simple way is to generate the years, then use join and aggregate:
select gs.yyyy, count(e.starting_date)
from public.table_of_events e left join
generate_series('2015-01-01'::date,
'2021-01-01'::date,
interval '1 year'
) as gs(yyyy)
on e.starting_date < gs.yyyy and
e.closing_date >= gs.yyyy + interval '1 year'
group by gs.yyyy;

How can I get a user's activity count for today and this month in a single SELECT query

In my table I have:
Activity : Date
---------------
doSomething1 : June 1, 2020
doSomething2 : June 14, 2020
I want to be able to make a query so that I can get the following result (assuming today is June 1, 2020):
Today : ThisMonth
1 : 2
I looked at group by but I wasn't sure how to do that without a lot of additional code and I think there's very likely a much simpler solution that I'm missing. Something that will just return a single row with two results. Is this possible and if so how?
you can write subqueries to get data in single row,
Select today , month
from
(
( query to get today's count ) as today,
( query to get month's count ) as month
) t;
yes, u can do group by on dates to get todays nd months count.
Hope this will give u some perception to go on.
Is this what you want?
select array_agg(activity) filter (where date = current_date) as today,
array_agg(activity) filter (where date <> current_date) as rest_of_month
from t
where date_trunc('month', date) = current_date;
This uses arrays so it can handle more than one activity in either category.
Assume you want to query based on a particular date -
select count(case when d.date = :p_query_date then 0 end) day_count
,count(0) month_count
from d -- your table name
where d.date between date_trunc('month', :p_query_date)
and date_trunc('month', :p_query_date + interval '1 month') - interval '1 day'
The above query assumes you have index defined on d.date column. If you have index defined on date_trunc('month', date), the query condition can be simplified to:
date_trunc('month', d.date) = date_trunc('month', :p_query_date)

Find Distinct IDs when the due date is always on the last day of each month

I have to find distinct IDs throughout the whole history of each ID whose due dates are always on the last day of each month.
Suppose I have the following dataset:
ID DUE_DT
1 1/31/2014
1 2/28/2014
1 3/31/2014
1 6/30/2014
2 1/30/2014
2 2/28/2014
3 1/29/2016
3 2/29/2016
I want to write a code in SQL so that it gives me ID = 1 as for this specific ID the due date is always on the last day of each given month.
What would be the easiest way to approach it?
You can do:
select id
from t
group by id
having sum(case when extract(day from due_dt + interval '1 day') = 1 then 1 else 0 end) = count(*);
This uses ANSI/ISO standard functions for date arithmetic. These tend to vary by database, but the idea is the same in all databases -- add one day and see if the day of the month is 1 for all the rows.
If your using SQL Server 2012+ you can use the EOMONTH() function to achieve this:
SELECT DISTINCT ID FROM [table]
WHERE DUE_DT = EOMONTH(DUE_DT)
http://rextester.com/VSPQR78701
The idea is quite simple:
you are on the last day of the month if (the month of due date) is not the same as (the month of due date + 1 day). This covers all cases across year, leap year and so on.
from there on, if (the count of rows for one id) is the same as (the count of rows for this id which are the last day of the month) you have a winner.
I tried to write an example (not tested). You do not specify which DB so I will assume that cte (common table expression) are available. If not just put the cte as subquery.
In the same way, I am not sure that dateadd and interval work the same in all dialect.
with addlastdayofmonth as (
select
id
-- adding a 'virtualcolumn', 1 if last day of month 0 otherwise
, if(month(dateadd(due_date, interval '1' day)) != month(due_date), 1 ,0) as onlastday
from
table
)
select
id
, count(*) - sum(onlastday) as alwayslastday
from
addlastdayofmonth
group by
id
having
-- if count(rows) == count(rows with last day) we have a winner
halwayslastday = 0
MySQL-Version (credits to #Gordon Linoff)
SELECT
ID
FROM
<table>
GROUP BY
ID
HAVING
SUM(IF(day(DUE_DT + interval 1 Day) = 1, 1, 0)) = COUNT(ID);
Original Answer:
SELECT MAX(DUE_DT) FROM <table> WHERE ID = <the desired ID>
or if you want all MAX(DUE_DT) for each unique ID
SELECT ID, MAX(DATE) FROM <table> GROUP BY ID

Postgres inner query performance

I have a table which I need to select from everything with this rule:
id = 4524522000143 and validPoint = true
and date > (max(date)- interval '12 month')
-- the max date, is the max date for this id
Explaining the rule: I have to get all registers and count them, they must be at least 1 year old from the newest register.
This is my actual query:
WITH points as (
select 1 as ct from base_faturamento_mensal
where id = 4524522000143 and validPoint = true
group by id,date
having date > (max(date)- interval '12 month')
) select sum(ct) from points
Is there a more efficient way for this?
Well your query is using the trick with including an unaggregated column within HAVING clause but I don't find it particularly bad. It seems fine, but without the EXPLAIN ANALYZE <query> output I can't say much more.
One thing to do is you can get rid of the CTE and use count(*) within the same query instead of returning 1 and then running a sum on it afterwards.
select count(*) as ct
from base_faturamento_mensal
where id = 4524522000143
and validPoint = true
group by id, date
having date > max(date) - interval '12 months'

Calculate closest working day in Postgres

I need to schedule some items in a postgres query based on a requested delivery date for an order. So for example, the order has a requested delivery on a Monday (20120319 for example), and the order needs to be prepared on the prior working day (20120316).
Thoughts on the most direct method? I'm open to adding a dates table. I'm thinking there's got to be a better way than a long set of case statements using:
SELECT EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40');
This gets you previous business day.
SELECT
CASE (EXTRACT(ISODOW FROM current_date)::integer) % 7
WHEN 1 THEN current_date-3
WHEN 0 THEN current_date-2
ELSE current_date-1
END AS previous_business_day
To have the previous work day:
select max(s.a) as work_day
from (
select s.a::date
from generate_series('2012-01-02'::date, '2050-12-31', '1 day') s(a)
where extract(dow from s.a) between 1 and 5
except
select holiday_date
from holiday_table
) s
where s.a < '2012-03-19'
;
If you want the next work day just invert the query.
SELECT y.d AS prep_day
FROM (
SELECT generate_series(dday - 8, dday - 1, interval '1d')::date AS d
FROM (SELECT '2012-03-19'::date AS dday) x
) y
LEFT JOIN holiday h USING (d)
WHERE h.d IS NULL
AND extract(isodow from y.d) < 6
ORDER BY y.d DESC
LIMIT 1;
It should be faster to generate only as many days as necessary. I generate one week prior to the delivery. That should cover all possibilities.
isodow as extract parameter is more convenient than dow to test for workdays.
min() / max(), ORDER BY / LIMIT 1, that's a matter of taste with the few rows in my query.
To get several candidate days in descending order, not just the top pick, change the LIMIT 1.
I put the dday (delivery day) in a subquery so you only have to input it once. You can enter any date or timestamp literal. It is cast to date either way.
CREATE TABLE Holidays (Holiday, PrecedingBusinessDay) AS VALUES
('2012-12-25'::DATE, '2012-12-24'::DATE),
('2012-12-26'::DATE, '2012-12-24'::DATE);
SELECT Day, COALESCE(PrecedingBusinessDay, PrecedingMondayToFriday)
FROM
(SELECT Day, Day - CASE DATE_PART('DOW', Day)
WHEN 0 THEN 2
WHEN 1 THEN 3
ELSE 1
END AS PrecedingMondayToFriday
FROM TestDays) AS PrecedingMondaysToFridays
LEFT JOIN Holidays ON PrecedingMondayToFriday = Holiday;
You might want to rename some of the identifiers :-).