Not able to get exact latest records with two columns having same value - in SQL Server - sql

I am trying to get distinct records for a specific department from the table employee.
I have tried with this code in SQL Server, and I'm getting this error:
Error: employeeId is invalid in the select list because it is not contained in either aggregate function or the GROUP BY clause.
My code:
SELECT
name, department, MAX(jointime) LatestDate, employeeId
FROM
employee
WHERE
department = 'Mechanical'
GROUP BY
name
Records in DB:
name department joinTime EmployeeId
-----------------------------------------------------------
Erik Mechanical 2019-07-06 11:59:59 456
Tom Mechanical 2019-07-06 11:59:59 789
Erik Computer 2019-07-05 11:59:59 222
Erik Computer 2019-07-04 11:59:59 111
Erik Mechanical 2019-07-01 11:59:59 123
I want to achieve the result when a query for 'Mechanical' is executed. The latest record should be fetched from DB for a particular department.
name department joinTime EmployeeId
-----------------------------------------------------------
Erik Mechanical 2019-07-06 11:59:59 456
Tom Mechanical 2019-07-06 11:59:59 789

Assuming the key is [Name] and not [EmployeeId]
One option is the WITH TIES clause, and thus no need for aggregation
Example
Select Top 1 with ties *
From employee
Where department='Mechanical'
Order By Row_Number() over (Partition By [Name] order by joinTime Desc)
Returns
name department joinTime EmployeeId
Erik Mechanical 2019-07-06 11:59:59.000 456
Tom Mechanical 2019-07-06 11:59:59.000 789

You can use EXISTS:
SELECT e.*
FROM employee e
WHERE e.department='Mechanical'
AND NOT EXISTS (
SELECT 1 FROM employee
WHERE department = e.department
AND name = e.name AND joinTime > e.joinTime
)
See the demo.
Results:
> name | department | joinTime | EmployeeId
> :--- | :--------- | :------------------ | ---------:
> Erik | Mechanical | 2019-07-06 11:59:59 | 456
> Tom | Mechanical | 2019-07-06 11:59:59 | 789

You can use ROW_NUMBER to mark the latest row for each employee, or CROSS APPLY to run a correlated subquery for each employee.
with q as
(
SELECT name, department, jointime, employeeId,
row_number() over (partition by name, order by joinTime desc) rn
FROM employee where department='Mechanical'
)
select name, department, jointime, employeeId
from q
where rn = 1
or
with emp as
(
select distinct name from employee
)
select e.*
from q
cross apply
(
select top 1 *
from employee e2
where e2.name = q.name
order by joinDate desc
) e

Just add department,employeeId to the GROUP BY
SELECT name , department, MAX(jointime) LatestDate , employeeId
FROM employee where department='Mechanical'
GROUP BY name, department, employeeId

You need to use AGGREGATE Functions for fields which are used in SELECT statement:
SELECT name,
MIN(department)
, MAX(jointime) LatestDate,
, MIN(employeeId)
FROM employee where department='Mechanical'
GROUP BY name
SQL server finds all records with names Tom or Erik, but SQL Server does not know what one value from multiple rows should be chosen for the fields such as department or employeeId. By using aggregrate functions, you are advising SQL Server to get the MIN, MAX, SUM, COUNT values of that columns.
OR use those columns to the GROUP BY clause to get all unique rows:
SELECT name
, department
, jointime
, employeeId
FROM employee where department='Mechanical'
GROUP BY name
, department
, jointime
, employeeId

Related

How do I return both values if the maximum if two rows are equal?

I need to find the persons with the maximum salaries in each department. I've got the code and found out the persons with the maximum salaries for each department. But then, when I looked at my data, there is another person that has the equal max value in the same department. Is there a way to return both persons' name?
example table:
Department Salary Name
Admin $1000 Amy
Admin $900 Ben
HR $1500 Cassy
HR $1500 Dan
I have tried this code:
SELECT department, Max(salary), name
FROM table
GROUP BY department
ORDER BY salary desc;
I've been getting Admin's person's details OK. But HR I can only get Cassy's name. Is there a way to get Dan's name in my output as well? Can anyone give me an example? Thank you
Hope this can help
SELECT department, salary, name
FROM table t
where salary= (select max(salary) from table where t.department = department)
You didn't mention the DBMS you are using.
With standard SQL, you can use window functions for this (which are supported by all modern DBMS):
select department, salary, name
from (
select department, salary, name,
dense_rank() over (partition by department order by salary desc) as rnk
from department
) t
where rnk = 1;
With NOT EXISTS:
SELECT department, salary, name
FROM tablename t
WHERE NOT EXISTS (
SELECT 1 FROM tablename
WHERE department = t.department and salary > t.salary
)
ORDER BY salary desc, name;
See the demo.
Results:
| Department | Salary | Name |
| ---------- | ------ | ----- |
| HR | 1500 | Cassy |
| HR | 1500 | Dan |
| Admin | 1000 | Amy |
You can use two levels of aggregation if you want one row per department with the names lists on the row:
select dept, salary, names
from (select dept, salary, group_concat(name) as names,
row_number() over (partition by dept order by salary desc) as seqnum
from example
group by dept, salary
) t
where seqnum = 1;

How to create a oracle view of the max sum of a sum of values of a column based on the values of another

I need to create a view in Oracle 11g that would take these tables:
employees
FirstName | LastName | EmployeeID
-----------------------------------
joe | shmo | 1
bob | moll | 2
salesData
Employee ID | commission on sale
----------------------------------
1 | $20
1 | $30
2 | $50
2 | $60
and then sum up the total commission each employee earned and return the employee who earned the most commission.
So using the sample data the view will contain the employee id :: 2 or bob moll.
This should get you what you need
Create someviewname as view
Select EmployeeID, sum (commision)
from employees
left outer join salesData on salesData.EmployeeID = employees.EmployeeID
Group by EmployeeID, commision
order by commission desc
SELECT employeeID
FROM
(SELECT employeeID,
SUM(commission)
FROM sales
GROUP BY employeeID
ORDER BY SUM(commission)
)
WHERE rownum = 1
Not sure why you want a view of that, but hopefully, you can figure that out.
In Oracle 12g+, you can use fetch:
select employeeid
from sales
group by employeeid
order by sum(commission) desc
fetch first 1 row only;
In earlier versions, one method is to use rownum:
select s.*
from (select employeeid
from sales
group by employeeid
order by sum(commission) desc
) s
where rownum = 1;

oracle duplicate rows based on a single column

How can I find out duplicate rows based on a single column. I have a table in oracle which has data as given below and it has duplicates. I'm trying to select and view all rows with duplicate employee ids as explained below
EMP table:
EmpId Fname Lname Mname Jobcode Status exp_date
1 Mike Jordan A IT W 12/2014
1 Mike Jordan A IT A 12/2014
2 Angela ruth C sales P 12/2015
2 Angela ruth C IT W 12/2015
3 Kelly Mike B sales W 12/2015
From the above table i want to select all rows which duplicate empids such as below
EmpId Fname Lname Mname Jobcode Status exp_date
1 Mike Jordan A IT W 12/2014
1 Mike Jordan A IT A 12/2014
2 Angela ruth C sales P 12/2015
2 Angela ruth C IT W 12/2015
How can I do this? thank you!
SELECT a.*
FROM TableName a
INNER JOIN
(
SELECT EmpID
FROM TableName
GROUP BY EmpID
HAVING COUNT(*) > 1
) b ON a.EmpID = b.EmpID
SQLFiddle Demo
Another way, although I prefer above, is to use IN
SELECT a.*
FROM TableName a
WHERE EmpId IN
(
SELECT EmpId
FROM TableName
GROUP BY EmpId
HAVING COUNT(*) > 1
)
SQLFiddle Demo
Here's another option using a subquery and COUNT OVER PARTITION BY since you're using Oracle 11:
SELECT *
FROM (
SELECT EmpId, Fname, Lname, Mname, Jobcode, Status, exp_date,
COUNT(EmpId) OVER (PARTITION BY EmpId) EmpCount
FROM TableName
) T
WHERE EmpCount > 1
SQL Fiddle Demo (Borrowed from JW)

SQL: select three rows for each distinct value

For each distinct Name, I want to select the first three rows with the earliest time_stamp (or smallest number in UNIXTIME). What is the correct query?
Start Table:
Name Log-in Time
-------- -----------------
Don 05:30:00
Don 05:35:32
Don 07:12:43
Don 09:52:23
Don 05:32:43
James 03:30:00
James 03:54:23
James 09:51:54
James 14:43:34
James 43:22:11
James 59:43:33
James 20:12:11
Mindy 05:32:22
Mindy 15:14:44
Caroline 10:02:22
Rebecca 20:43:32
End Table:
Name Log-in Time
-------- -----------------
Don 05:30:00
Don 05:35:32
Don 07:12:43
James 03:30:00
James 03:54:23
James 09:51:54
Mindy 05:32:22
Mindy 15:14:44
Caroline 10:02:22
Rebecca 20:43:32
WITH Table (Name, LoginTime, Row) AS
(
SELECT
Name,
LoginTime,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY LoginTime)
FROM SomeTable
)
SELECT
Name,
LoginTime
FROM Table
WHERE
Row <= 3
An ansi standard approach actually looks to work with the following:
http://www.sqlfiddle.com/#!2/b814d/15
SELECT
NAME
, LOGIN
FROM (
SELECT
test_first.NAME,
test_first.LOGIN,
COUNT(*) CNT
FROM
TABLE_NAME test_first
LEFT OUTER JOIN
TABLE_NAME test_second
ON (test_first.NAME = test_second.NAME)
WHERE
test_first.LOGIN <= test_second.LOGIN
GROUP BY
test_first.NAME, test_first.LOGIN) test_order
WHERE
test_order.CNT <= 3
ORDER BY
NAME ASC, LOGIN ASC

Get top results for each group (in Oracle)

How would I be able to get N results for several groups in
an oracle query.
For example, given the following table:
|--------+------------+------------|
| emp_id | name | occupation |
|--------+------------+------------|
| 1 | John Smith | Accountant |
| 2 | Jane Doe | Engineer |
| 3 | Jack Black | Funnyman |
|--------+------------+------------|
There are many more rows with more occupations. I would like to get
three employees (lets say) from each occupation.
Is there a way to do this without using a subquery?
I don't have an oracle instance handy right now so I have not tested this:
select *
from (select emp_id, name, occupation,
rank() over ( partition by occupation order by emp_id) rank
from employee)
where rank <= 3
Here is a link on how rank works: http://www.psoug.org/reference/rank.html
This produces what you want, and it uses no vendor-specific SQL features like TOP N or RANK().
SELECT MAX(e.name) AS name, MAX(e.occupation) AS occupation
FROM emp e
LEFT OUTER JOIN emp e2
ON (e.occupation = e2.occupation AND e.emp_id <= e2.emp_id)
GROUP BY e.emp_id
HAVING COUNT(*) <= 3
ORDER BY occupation;
In this example it gives the three employees with the lowest emp_id values per occupation. You can change the attribute used in the inequality comparison, to make it give the top employees by name, or whatever.
Add RowNum to rank :
select * from
(select emp_id, name, occupation,rank() over ( partition by occupation order by emp_id,RowNum) rank
from employee)
where rank <= 3
tested this in SQL Server (and it uses subquery)
select emp_id, name, occupation
from employees t1
where emp_id IN (select top 3 emp_id from employees t2 where t2.occupation = t1.occupation)
just do an ORDER by in the subquery to suit your needs
I'm not sure this is very efficient, but maybe a starting place?
select *
from people p1
join people p2
on p1.occupation = p2.occupation
join people p3
on p1.occupation = p3.occupation
and p2.occupation = p3.occupation
where p1.emp_id != p2.emp_id
and p1.emp_id != p3.emp_id
This should give you rows that contain 3 distinct employees all in the same occupation. Unfortunately, it will give you ALL combinations of those.
Can anyone pare this down please?