how to 'CASE WHEN DATEDIFF(...) THEN DATEDIFF(..)..' without grouping by date - sql

Using this query:
select a.username, count(*)
from A a inner join (
/* duplicate results will be removed by the union */
select username, date from B union
select username, date from C union
select username, date from D union
select username, date from E union
select username, date from F
) u on u.username = a.username
where a.min_date >= u.date
group by username;
which gets the count of unique dates across some tables and groups them by username.
I want to get the difference of the first date and the last date for each username within an 30 day interval i.e. something like this (but obviously it's not working):
CASE WHEN DATEDIFF(DAY, min_date, [Date]) <= 30
THEN DATEDIFF(DAY, min([Date]), max([Date])) ELSE NULL END as diff
which tells me I need to groupby min_date and [Date], but this is something I believe I don't want to do. Is there a workaround? If I use some aggregate function on case e.g. MAX(CASE... then I get the cannot use aggregated function containing an aggregate function.

Related

Compare number of users on year ago

I want to get the count of distinct current users and one year ago using sql I have used the following query but it gets the current number and zero for one year ago values.
select CAST(root_tstamp as DATE) as Date,
count(DISTINCT userid) as sign_ups,
count(Distinct case when CAST(root_tstamp as DATE) = ADD_MONTHS(CAST(root_tstamp as
DATE),-12) then userid end) as Last_year_Signups
FROM table1
I have added sql server code.
For other type of DBs if there are any difference in syntax it will be only in date_add function.
;with table1 as(select '2022-01-25 13:14:37.840' as root_tstamp,1 as userid
union select '2022-01-25 13:14:37.840' as root_tstamp,2 as userid
union select '2021-01-25 13:14:37.840' as root_tstamp,1 as userid
union select '2021-01-25 13:14:37.840' as root_tstamp,2 as userid
union select '2022-01-25 13:14:37.840' as root_tstamp,1 as userid
)
select CAST(A.root_tstamp as DATE)as date
,count(DISTINCT a.userid) as sign_ups
,count(Distinct dateadd(MONTH,-12,CAST(B.root_tstamp as DATE))) as Last_year_Signups
from table1 a
left join table1 b on a.userid = b.userid and CAST(A.root_tstamp as DATE) = dateadd(MONTH,12, CAST(B.root_tstamp as DATE))
Result
date
sign_ups
Last_year_Signups
2021-01-25
2
0
2022-01-25
2
1

Want a SQL statement to count number

I have a table have 3 columns id, open_time, close_time, the data looks like this:
then I want a SQL to get result like this:
the rule is : if the date equals to open time then New, if the date > open_time and date < close_time then Open, if the date equals close_time then Closed
how can I write the SQL in Oracle?
First build a table on-the-fly containing all dates from the minimum date in the table until today. You need a recursive query for this.
Then build a table on-the-fly for the three statuses.
Now cross join the two to get all combinations. These are the rows you want.
The rest is counting per day and status, which can be achieved with a join and grouping or with one or more subqueries. I'm showing the join:
with days(day) as
(
select min(open_time) as day from opentimes
union all
select day + 1 from days where day < trunc(sysdate)
)
, statuses as
(
select 'New' as status, 1 as sortkey from dual
union all
select 'Open' as status, 2 as sortkey from dual
union all
select 'Close' as status, 3 as sortkey from dual
)
select
d.day,
s.status,
count(case when (s.status = 'New' and d.day = o.open_time)
or (s.status = 'Open' and d.day = o.close_time)
or (s.status = 'Close' and d.day > cls.open_time and d.day < cls.close_time)
then 1 end) as cnt
from days d
cross join statuses s
join opentimes o on d.day between o.open_time and o.close_time
group by d.day, s.status
order by d.day, max(s.sortkey);

SQL Server: Attempting to output a count with a date

I am trying to write a statement and just a bit puzzled what is the best way to put it together. So I am doing a UNION on a number of tables and then from there I want to produce as the output a count for the UserID within that day.
So I will have numerous tables union such as:
Order ID, USERID, DATE, Task Completed.
UNION
Order ID, USERID, DATE, Task Completed
etc
Above is layout of the table which will have 4 tables union together with same names.
Then statement output I want is for a count of USERID that occurred within the last 24 hours.
So output should be:
USERID--- COUNT OUTPUT-- DATE
I was attempting a WHERE statement but think the output is not what I am after exactly, just thinking if anyone can point me in the right direction and if there is alternative way compared to the union? Maybe a joint could be a better alternative, any help be appreciated.
I will eventually then put this into a SSRS report, so it gets updated daily.
You can try this:
select USERID, count(*) as [COUNT], cast(DATE as date) as [DATE]
from
(select USERID, DATE From SomeTable1
union all
select USERID, DATE From SomeTable2
....
) t
where DATE <= GETDATE() AND DATE >= DATEADD(hh, -24, GETDATE())
group by USERID, cast(DATE as date)
First, you should use union all rather than union. Second, you need to aggregate and use count distinct to get what you want:
So, the query you want is something like:
select count(distinct userid)
from ((select date, userid
from table1
where date >= '2015-05-26'
) union all
(select date, userid
from table2
where date >= '2015-05-26'
) union all
(select date, userid
from table3
where date >= '2015-05-26'
)
) du
Note that this hardcodes the date. In SQL Server, you would do something like:
date >= cast(getdate() - 1 as date)
And in MySQL
date >= date_sub(curdate(), interval 1 day)
EDIT:
I read the question as wanting a single day. It is easy enough to extend to all days:
select cast(date as date) as dte, count(distinct userid)
from ((select date, userid
from table1
) union all
(select date, userid
from table2
) union all
(select date, userid
from table3
)
) du
group by cast(date as date)
order by dte;
For even more readability, you could use a CTE:
;WITH cte_CTEName AS(
SELECT UserID, Date, [Task Completed] FROM Table1
UNION
SELECT UserID, Date, [Task Completed] FROM Table2
etc
)
SELECT COUNT(UserID) AS [Count] FROM cte_CTEName
WHERE Date <= GETDATE() AND Date >= DATEADD(hh, -24, GETDATE())
I think this is what you are trying to achieve...
Select
UserID,
Date,
Count(1)
from
(Select *
from table1
Union All
Select *
from table2
Union All
Select *
from table3
Union All
Select *
from table4
) a
Group by
Userid,
Date

SQL using the result of a UNION query in another query

How can I complete this query?
Right now, the query I have working is this, but it is not producing the right data.
SELECT date, coalesce(count,0) AS count
FROM
generate_series(
'2014-12-13 12:00:00'::timestamp,
'2015-01-06 11:00:00'::timestamp,
'1 hour'::interval
) AS date
LEFT OUTER JOIN (
SELECT
date_trunc('day', TABLE1.created_at) as day,
count(DISTINCT TABLE1.user) as count
FROM TABLE1
WHERE org_id = 1
GROUP BY day
) results ON (date = results.day);
Instead of TABLE1, I need to feed the query with data from another query which looks like this:
SELECT TABLE2.user_a as userid, TABLE2.created_at as createdat from TABLE2
UNION ALL
SELECT TABLE3.user_b as userid, TABLE3.created_at as createdat from TABLE3
UNION ALL
SELECT TABLE4.sender as userid, TABLE4.created_at as createdat from TABLE4;
How do I do this?
Any part of a select query that receives a table (e.g., a from clause, a join clause, etc) can receive a query surrounded in parenthesis - this is called a subquery. Note that in Postgres this subquery must be given an alias (i.e., a name that it can be referenced by). So in your case:
SELECT date, coalesce(count,0) AS count
FROM
generate_series(
'2014-12-13 12:00:00'::timestamp,
'2015-01-06 11:00:00'::timestamp,
'1 hour'::interval
) AS date
LEFT OUTER JOIN (
SELECT
date_trunc('day', subquery.created_at) as day,
count(DISTINCT subquery.user) as count
-- Subquery here:
FROM (SELECT TABLE2.user_a as userid, TABLE2.created_at as createdat from TABLE2
UNION ALL
SELECT TABLE3.user_b as userid, TABLE3.created_at as createdat from TABLE3
UNION ALL
SELECT TABLE4.sender as userid, TABLE4.created_at as createdat from TABLE4)
subquery
WHERE org_id = 1
GROUP BY day
) results ON (date = results.day);

Total Count of Active Employees by Date

I have in the past written queries that give me counts by date (hires, terminations, etc...) as follows:
SELECT per.date_start AS "Date",
COUNT(peo.EMPLOYEE_NUMBER) AS "Hires"
FROM hr.per_all_people_f peo,
hr.per_periods_of_service per
WHERE per.date_start BETWEEN peo.effective_start_date AND peo.EFFECTIVE_END_DATE
AND per.date_start BETWEEN :PerStart AND :PerEnd
AND per.person_id = peo.person_id
GROUP BY per.date_start
I was now looking to create a count of active employees by date, however I am not sure how I would date the query as I use a range to determine active as such:
SELECT COUNT(peo.EMPLOYEE_NUMBER) AS "CT"
FROM hr.per_all_people_f peo
WHERE peo.current_employee_flag = 'Y'
and TRUNC(sysdate) BETWEEN peo.effective_start_date AND peo.EFFECTIVE_END_DATE
Here is a simple way to get started. This works for all the effective and end dates in your data:
select thedate,
SUM(num) over (order by thedate) as numActives
from ((select effective_start_date as thedate, 1 as num from hr.per_periods_of_service) union all
(select effective_end_date as thedate, -1 as num from hr.per_periods_of_service)
) dates
It works by adding one person for each start and subtracting one for each end (via num) and doing a cumulative sum. This might have duplicates dates, so you might also do an aggregation to eliminate those duplicates:
select thedate, max(numActives)
from (select thedate,
SUM(num) over (order by thedate) as numActives
from ((select effective_start_date as thedate, 1 as num from hr.per_periods_of_service) union all
(select effective_end_date as thedate, -1 as num from hr.per_periods_of_service)
) dates
) t
group by thedate;
If you really want all dates, then it is best to start with a calendar table, and use a simple variation on your original query:
select c.thedate, count(*) as NumActives
from calendar c left outer join
hr.per_periods_of_service pos
on c.thedate between pos.effective_start_date and pos.effective_end_date
group by c.thedate;
If you want to count all employees who were active during the entire input date range
SELECT COUNT(peo.EMPLOYEE_NUMBER) AS "CT"
FROM hr.per_all_people_f peo
WHERE peo.[EFFECTIVE_START_DATE] <= :StartDate
AND (peo.[EFFECTIVE_END_DATE] IS NULL OR peo.[EFFECTIVE_END_DATE] >= :EndDate)
Here is my example based on Gordon Linoff answer
with a little modification, because in SUBSTRACT table all records were appeared with -1 in NUM, even if no date was in END DATE = NULL.
use AdventureWorksDW2012 --using in MS SSMS for choosing DATABASE to work with
-- and may be not work in other platforms
select
t.thedate
,max(t.numActives) AS "Total Active Employees"
from (
select
dates.thedate
,SUM(dates.num) over (order by dates.thedate) as numActives
from
(
(
select
StartDate as thedate
,1 as num
from DimEmployee
)
union all
(
select
EndDate as thedate
,-1 as num
from DimEmployee
where EndDate IS NOT NULL
)
) AS dates
) AS t
group by thedate
ORDER BY thedate
worked for me, hope it will help somebody
I was able to get the results I was looking for with the following:
--Active Team Members by Date
SELECT "a_date",
COUNT(peo.EMPLOYEE_NUMBER) AS "CT"
FROM hr.per_all_people_f peo,
(SELECT DATE '2012-04-01'-1 + LEVEL AS "a_date"
FROM dual
CONNECT BY LEVEL <= DATE '2012-04-30'+2 - DATE '2012-04-01'-1
)
WHERE peo.current_employee_flag = 'Y'
AND "a_date" BETWEEN peo.effective_start_date AND peo.EFFECTIVE_END_DATE
GROUP BY "a_date"
ORDER BY "a_date"