Use of Group BY in more than 1 column - sql

I have made an table which has 3 columns
Table
In this above table there are 3 columns i.e Trandsaction, EmpID, Date having some data. I need to form a table using some queries so that in the result table I will get how many transactions done in each month by a particular EmpID.
So the result table must be like:
So how to get month wise number of transactions per EmpID?

Get all employees (I assume you have an employee table).
Get all months (you could get them from the transaction table).
Cross join the two in order to get all combinations, because you want to show these in your result.
Outer join the transaction counts per month and employee (an aggregation subquery). SQL Server's date to string conversion is a bit awkward compared to other DBMS. You need to convert to a predefined format and use a substring of that.
Use COALESCE to turn null (for no count for the employee and month) to zero.
The query:
select m.month, e.empid, coalesce(t.cnt, 0) as transaction_count
from employees e
cross join (select distinct convert(varchar(7), t.date, 126) as month from transactions) m
left join
(
select convert(varchar(7), t.date, 126) as month, empid, count(*) as total
from transactions
group by convert(varchar(7), t.date, 126), empid
) t on t.empid = e.empid and t.month = m.month
order by m.month, e.empid;
If you don't want all employees, but only those that have at least one transaction in some month, then replace from employees e with from (select distinct empid from transactions) e.

SELECT Date, EmpID, COUNT(transaction)
FROM your_table
GROUP BY Date, EmpID
To get the date like "YYYY-MM" use
for MySQL (replace the String with the date column):
DATE_FORMAT("2019-11-25 10:49:30.000", "%Y - %m")
for oracle:
to_char(date, 'YYYY-MM')

Related

Return group of columns with highest date

I have a table called events. I need to return all the group of distinct rows with maximum value for time_of_event in a month. For example, if you take October 2018 as time of event, I need to return all the distinct group of rest of the columns who has the highest date in Oct 2018 for column time_of_event.
OK. The key thing is to identify the relevant time_of_event for each month. Then we can join that to the list to get all events.
To identify the latest time_of_event, you can use a group-by statement e.g.,
SELECT MAX(time_of_event) AS Last_time_of_event
FROM events_new
GROUP BY YEAR(time_of_event), MONTH(time_of_event)
The above provides a list of all the last date/time of the events. If you want to filter the result set, feel free to do so (e.g., add a WHERE time_of_event >= '20180101' AND time_of_event < '20190101')
We can then use this as a sub-query or CTE to link to the original table to get all the events e.g.,
; WITH LTOE AS
(SELECT MAX(time_of_event) AS Last_time_of_event
FROM events_new
GROUP BY YEAR(time_of_event), MONTH(time_of_event)
)
SELECT DISTINCT en.User_ID, en.Date_Joined, en.Time_of_event, en.Event
FROM events_new en
INNER JOIN LTOE on en.Last_time_of_event = en.time_of_event
(edit: fixed typo in GROUP BY and added DISTINCT)
You could use the DENSE_RANK windowing function. Something like this
with e_cte as (
select *, dense_rank() over (partition by year(time_of_event), month(time_of_event)
order by time_of_event desc) dr
from [events])
select distinct [user_id], date_joined, time_of_event
from e_cte
where dr=1;
I think I would just use window functions and select distinct:
select distinct en.*
from (select e.*, max(time_of_event) over () as max_time_of_event
from events_new en
where time_of_event >= '2018-10-01' and
time_of_event < '2018-11-01'
) en
where time_of_event = max_time_of_event

Retrieving Date function in sql

I have an employee table with the hire_date column.
I am stuck with one query related to the date function, where I have used data type 'DATE' to insert date of hiring and using DATE_FORMAT fun. to retrieve no. of employees hired in every month, but in SQL-server it is not supporting the date_format function.
I'm using SQL -server
Query: - list of the no.of employee hired every month in ascending order.
select date_format(hire_date,'%b') month, count(*)
from employee
group by DATE_FORMAT(hire_date,'%b')
order by month
date_format(hire_date,'%b') in MySQL will return abbreviated monthname. However, you can still have this functionality by combining MONTHNAME with LEFT in SQL Server.
select LEFT(DATENAME(MONTH,hire_date),3) month, count(*)
from employee
group by LEFT(DATENAME(MONTH,hire_date),3)
order by month
select Month(Hire_Date),count('x') 'count' from employee
group by Month(Hire_Date)
order by Month(Hire_Date) asc
Instead, you can directly use:
select MONTH(hire_date), count(*)
from employee
group by MONTH(hire_date)
order by MONTH(hire_date)
or
select hire_date.MONTH, count(*)
from employee
group by hire_date.MONTH
order by hire_date.MONTH

refering to field out of subeselects scope

I'm working on a piece of SQL at the moment and i need to retrieve every row of a dataset with a median and an average aggregated in it.
Example
i have the following set
ID;month;value
and i would like to retrieve something like :
ID;month;value;average for this month;median for this month
without having to group by my result.
So it would be something like :
SELECT ID,month,value,
(SELECT AVG(value) FROM myTable) as "myAVG"
FROM myTable
but i would need that average to be the average for that month specifically. So, rows where the month="January" will have the average and median for "January" etc ...
Issue here is that i did not find a way to refer to the value of month in my subquery
(SELECT AVG(value) FROM myTable)
Does someone have a clue?
P.S: It's a redshift database i'm working on.
You would need to select all rows from the table, and do a left join with a select statement that does group by month. This way, you would get every row, and the group by results with them for that month.
Something like this:
SELECT * FROM myTable a
LEFT JOIN
(
SELECT Month, Sum(value being summed) as mySum
FROM myTable
GROUP BY Month
) b
ON a.Month = b.Month
Helpful?
with myavg as
(SELECT month, AVG(value) as avgval FROM myTable group by month)
, mymed as
(select month, median(value) as medval from myTable group by month)
select ID, month, value, ma.avgval, mm.medval
from mytable m left join myavg ma
on m.month = ma.month
left join mymed mm
on m.month = mm.month
You can use a cte to do this. However, you need a group by on month, as you are calculating an aggregate value.
In Redshift you can use Window Function.
select month,
avg(value) over
(PARTITION BY month rows unbounded preceding) as avg
from myTable
order by 1;

SQL - select max of datetime by day

Using the below example, I want to get a count of records grouped by Person and Product, for each day.
The example on the left is the data in the SQL Server 2008 database. The data on the right is my desired query result.
I could just substring off the time portion of the date values, then just do a Group By... but ideally the Max(date) would contain the full timestamp...
Is this what you want?
select t.person, t.product, count(*) as cnt, max(t.date) as date
from table t
group by t.person, t.product, cast(t.date as date);

SQL Count Of Open Orders Each Day Between Two Dates

I've tried searching but it's likely I'm using the wrong keywords as I can't find an answer.
I'm trying to find the number of orders that are open between two dates and by employee. I have one table that shows a list of employees, another that shows a list of orders that contains an open and close date and also a dates table if that helps.
The employee and order tables joined will return something like:
employee order ref opened closed
a 123 01/01/2012 04/01/2012
b 124 02/01/2012 03/01/2012
a 125 02/01/2012 03/01/2012
And I need to transform this data into:
Date employee Count
01/01/2012 a 1
02/01/2012 a 2
02/01/2012 b 1
03/01/2012 a 2
03/01/2012 b 1
04/01/2012 a 1
I'm pulling the data from SQL server.
Any ideas?
Thanks
Nick
Join Dates to the result of the join between Employees and Orders, then group by dates and employees to obtain the counts, something like this:
SELECT
d.Date,
o.Employee,
COUNT(*) AS count
FROM Employees e
INNER JOIN Orders o ON e.ID = o.Employee
INNER JOIN Dates d ON d.Date BETWEEN o.Opened AND o.Closed
GROUP BY
d.Date,
o.Employee
My favorite way to do this counts the number of cumulative opens and the number of cumulative closes over time.
with cumopens as
(select employee, opened as thedate,
row_number() over (partition by employee order by opened) as cumopens,
0 as cumcloses
from eo
),
cumcloses as
(select employee, closed as thedate, 0 as cumopens,
row_number() over (partition by employee order by closed ) as cumcloses
from eo
)
select employee, c.thedate, max(cumopens), max(cumcloses),
max(cumopens) - max(cumcloses) as stillopened
from ((select *
from cumopens
) union all
(select *
from cumcloses
)
) c
group by employee, thedate
The only problem with this approach is that only dates where there is employee activity get reported. This works in your case.
The more general solution requires a sequence numbers to generate dates. For this, I often create one from some existing table with enough rows:
with nums as
(select row_number() over (partition by null order by null) as seqnum
from employees
)
select employee, dateadd(day, opened, seqnum) as thedate, count(*)
from eo join
nums
on datediff(day, opened, closed) < seqnum
group by employee, dateadd(day, opened, seqnum)
order by 1, 2
SELECT opened,employee,count(*)
FROM employee LEFT JOIN orders
WHERE opened < firstDate and opened > secondDate
GROUP BY opened,employee
or you can change the first condition in
WHERE opened BETWEEN firstDate and secondDate
Calling the result column count was a bit odd because it seems to be in fact a row number.
You can do that by using ROW_NUMBER.
The other interesting part is that you also want open date and close date as separate rows. Using a simple UNION will solve that.
WITH cte
AS (SELECT Row_number() OVER ( PARTITION BY employee
ORDER BY order_ref) count,
employee,
opened,
closed
FROM orders)
SELECT employee, opened date, count
FROM cte
UNION ALL
SELECT employee, closed date, count
FROM cte
ORDER BY Date,
employee
DEMO