Select multiple distincts with group by (I think) - sql

I was wondering if someone could help me with this. Here is a sample set of my data:
FirstName LastName Department Comment DateWorked
Bob Smith Sleeves 2017-01-01
Jim Bo Sleeves 2017-01-01
Janet Moore Lids No Show 2017-01-01
Jon Bob Lids 2017-01-01
Bob Smith Mugs 2017-01-02
Bob Smith Sleeves 2017-01-03
Jim Bo Sleeves 2017-01-03
Janet Moore Lids 2017-01-03
Jon Bob Lids 2017-01-03
It should return something like this:
DateWorked Department HeadCount
2017-01-01 Sleeves 2
2017-01-01 Lids 2
2017-01-02 Mugs 1
2017-01-03 Sleeves 2
2017-01-03 Lids 2
So far I've tried a few things.
This is what I want but it's not working
SELECT Count(Distinct(FirstName, LastName, Department, Scheduled), Notes) FROM Employees
Where Scheduled < 20171231
and Scheduled > 20170101
Group by Scheduled, Department, FirstName, LastName, Department, Comment
This just gives me a number.
select count(*)
from
(select count(*) CT
from Employees
group by Scheduled, Department) TD
This errors out.
SELECT COUNT(DISTINCT FirstName, LastName, Department) FROM Employees
Can anyone help?
Thanks

You would appear to want:
SELECT DateWorked, Department, COUNT(*) as HeadCount
FROM Employees
WHERE Scheduled < 20171231 AND Scheduled > 20170101
GROUP BY DateWorked, Department
ORDER BY DateWorked, Department;
The above keeps the date comparisons as you have in your query, although they seem wrong.
I would recommend writing the date comparisons as:
SELECT DateWorked, Department, COUNT(*) as HeadCount
FROM Employees
WHERE Scheduled >= '2017-01-01' AND Scheduled < '2017-12-31'
GROUP BY DateWorked, Department
ORDER BY DateWorked, Department;
This fixes the date comparisons, to be more aligned with your desired results.
You don't specify your database. YYYY-MM-DD is the ISO standard date format and works across most databases.

SELECT DateWorked, Department, COUNT(*) AS HeadCount
FROM Employees
WHERE Scheduled < 20171231 AND Scheduled > 20170101
GROUP BY Department, DateWorked

Related

Locating Historical records using Oracle PL/SQL

I have the following Oracle employee table which tracks employee movement between various departments.
EMPID DEPARTMENT RECORD_DATE
123456 Technology 2019-01-01
123456 Technology 2019-02-25
123456 Finance 2019-03-01
123456 Finance 2019-09-28
123456 HR 2020-03-01
987654 HR 2019-04-01
987654 Finance 2019-09-01
987654 HR 2020-01-31
I need to write an Oracle PL/SQL script that will allow the user to define a department name, and a historical date, resulting in having the query display all employees that were assigned to that department at specific point in time.
Example: If I wanted to know all the employees that worked in Finance on 2019-10-01, the query would return:
EMPID DEPARTMENT DEPARTMENT_START_DATE
123456 Finance 2019-03-01
987654 Finance 2019-09-01
(Note, a Department "leave" date would be nice, but optional)
Any ideas?
You can use window functions for this:
select empid, department, record_date
from (
select
t.*,
lead(record_date) over(partition by empid order by record_date) lead_record_date
from mytable t
) t
where
department = :department_id
and :target_date >= record_date
and (:target_date < lead_record_date or lead_record_date is null)
:department_id and :target_date represent the parameters to the query.
In the subquery, lead() retrieves the "next" record_date of the same empid. The outer query uses that information as a filter parameter to locate the relevant records.
With your given example it seems ,you need employee list with historical date for each department(if worked).This can be achieved with simple subquery
select empid,record_date from(
select empid,department,record_Date,ROW_NUMBER() over (partition by empid,department order by empid) as id
from employee)a
where id=1
order by 3

Oracle SQL code to list a persons employment events and another 2 columns devoted listing the prior event and date

So I have a database full of people with employment events and I'm trying to build a report in SQL that will pull the following:
Name, employment event, date of employment event, and the employment event that occurred prior to that event, and date of most recent event.
The data is organized so that each event is a row. So if I pull the employment history for a participant named John Smith I would get the output (sorted by date of event desc):
Name Event Date of Event
John Smith Terminated 5/13/2017
John Smith Return from Leave 4/13/2017
John Smith Paid Leave 3/31/2017
John Smith Hire 1/1/2000
My goal is to get the following output:
Name Event Date of Event Prior Event Date of prior event
John Smith Terminated 5/13/2017 Return from Leave 4/13/2017
John Smith Return from Leave 4/13/2017 Paid Leave 3/31/2017
John Smith Paid Leave 3/31/2017 Hire 1/11/2000
John Smith Hire 1/1/2000 NULL NULL
I managed to get a code working that almost does this.
select distinct a.ssn, b.name, a.event, a.event_date,
c.event as prior_event, c.event_date as prior_date
from history a
left join basic_data b
on b.ssn = a.ssn
Left Join
(select distinct c.ssn, c.event_date, c.event
from history c
) c
on c.ssn = a.ssn and (a.event > c.event)
order by a.ssn asc, a.event_date desc
That gives me this output:
Name Event Date of Event Prior Event Date of prior event
John Smith Terminated 5/13/2017 Return from Leave 4/13/2017
John Smith Terminated 5/13/2017 Paid Leave 3/31/2017
John Smith Terminated 5/13/2017 Hire 1/1/2000
John Smith Return from Leave 4/13/2017 Paid Leave 3/31/2017
John Smith Return from Leave 4/13/2017 Hire 1/1/2000
John Smith Paid Leave 3/31/2017 Hire 1/1/2000
John Smith Hire 1/1/2000 NULL NULL
It's showing multiple rows for every event prior to that event instead of just the one before it. How do I get rid of all of the extra rows?
You can use lead() analytic function with order by event_date desc
select h.name as "Name", h.event as "Event", h.event_date as "Date of Event",
lead(h.event) over (order by event_date desc) as "Prior Event",
lead(h.event_date) over (order by event_date desc) as "Date of prior event"
from history h
order by event_date desc;
Demo
If you want the prior events use lag(). Also, partitioning by the employee is very important:
select h.name, h.event, h.event_date,
lag(h.event) over (partition by h.name order by h.event_date) as prior_event,
lag(h.event_date) over (order by event_date) as prior_event-date
from history h
order by h.name, event_date desc;

Multiple Group By and Count

I was wondering if someone could help me with this. Here is a sample set of my data:
FirstName LastName Department Ticket Hours Shift DateWorked Key
Bob Smith Sleeves 23235 4 1 2017-01-01 001
Bob Smith Sleeves 12345 4 1 2017-01-01 001
Jim Bo Sleeves 12345 8 1 2017-01-01 002
Janet Moore Lids 78945 8 2 2017-01-01 003
Jon Bob Lids 45621 1.5 3 2017-01-01 004
Jon Bob Lids 45621 7.5 3 2017-01-01 004
Bob Smith Mugs 12345 8 1 2017-01-02
Jim Bo Lids 99999 8 3 2017-01-02
It should return something like this:
DateWorked Shift Department HeadCount
2017-01-01 1 Sleeves 2 (Bob Smith has two entries but counted as one and Jim Bo makes for 2)
2017-01-01 2 Lids 1 (Janet)
2017-01-01 3 Lids 1 (Jon)
Please note that all departments work all shifts. This is just a sample set. There can be anywhere from none to a hundred per department.
Also one employee could work multiple departments in one day! I don't know how to account for that.
This is what I have. So for this example it's not summing Bob Smith. It's counting him as two.
SELECT Scheduled, Department, [Shift], COUNT(*) as HeadCount
FROM EmployeeTickets
WHERE Scheduled >= '2017-01-01' AND Scheduled < '2017-12-31'
GROUP BY Scheduled, Department, [Shift]
ORDER BY Scheduled, Department, [Shift]
Thank you.
ETA I don't know if it helps but in the table there is a key per entry, so Bob Smith on Jan 1 would have a key for that day. His social security number is also in there. I'm trying to group by one of those somehow.
just use DISTINCT
SELECT Scheduled, Department, [Shift], COUNT( DISTINCT FirstName ) as HeadCount
FROM EmployeeTickets
WHERE Scheduled >= '2017-01-01' AND Scheduled < '2017-12-31'
GROUP BY Scheduled, Department, [Shift]
ORDER BY Scheduled, Department, [Shift]
Of course this will have problem if you have multiple persons with same name. So I hope you have some EmployeeID on your tables, so you can differentiate every employee.
COUNT(DISTINCT EmployeeID)

Advanced Sql query solution required

player team start_date end_date points
John Jacob SportsBallers 2015-01-01 2015-03-31 100
John Jacob SportsKings 2015-04-01 2015-12-01 115
Joe Smith PointScorers 2014-01-01 2016-12-31 125
Bill Johnson SportsKings 2015-01-01 2015-06-31 175
Bill Johnson AllStarTeam 2015-07-01 2016-12-31 200
The above table has many more rows. I was asked the below questions in an interview.
1.)For each player, which team were they play for on 2015-01-01?
I could not answer this one.
2.)For each player, how can we get the team for whom they scored the most points?
select team from Players
where points in (select max(points) from players group by player).
Please, solutions for both.
1
select *
from PlayerTeams
where startdate <='2015-01-01' and enddate >= '2015-01-01'
2
Select player, team, points
from(
Select *, row_number() over (partition by player order by points desc) as rank
From PlayerTeams) as player
where rank = 1
For #1:
Select Player
,Team
From table
Where '2015-01-01' between start_date and end_date
For #2:
select t.Player
,t.Team
from table t
inner join (select Player
,Max(points)
from table
group by Player) m
on t.Player = m.Player
and t.points = m.points

Is there a better way to do this join?

I have a table of my sales agents' sales, by quarter:
Agent Quarter Sales
----------------------------
Alex Andersen 2011Q1 358
Alex Andersen 2011Q2 289
Alex Andersen 2011Q3 27
Alex Andersen 2011Q4 2965
Brian Blogg 2010Q3 277
Brian Blogg 2010Q4 123
Brian Blogg 2011Q1 783
Brian Blogg 2011Q2 0
Christy Cliff 2011Q2 777
Christy Cliff 2011Q3 273
Christy Cliff 2011Q4 111
Christy Cliff 2012Q1 901
What's the simplest, most efficient query for getting each agent's earliest quarter and the sales for that quarter?
It's easy to find out "What is each agent's first quarter?":
SELECT agent, min(quarter) FROM salestable GROUP BY agent
But this doesn't include the sales figures, so I thought I'd do a join:
SELECT agent, sales
FROM salestable s1
JOIN
(
SELECT agent AS e, MIN(quarter) AS q
FROM salestable
GROUP by employee
) AS q1 ON q1.e=s1.agent AND q1.mq=s1.quarter
But this is unacceptably slow on my data set. If I could use a cursor, it would only take one pass through the table, but using a query it seems to require a join. Is that right?
Try this variation and see if it's any better:
WITH cteRowNum AS (
SELECT agent, quarter, sales,
ROW_NUMBER() OVER (PARTITION BY agent ORDER BY quarter) AS RowNum
FROM salestable
)
SELECT agent, quarter, sales
FROM cteRowNum
WHERE RowNum = 1;