How to retrieve data in the same order as the subquery? - sql

Let us say Employee and Shippers tables have following data.
EmployeeID Name
1 Davolio
2 Fuller
3 Leverling
4 Peacock
5 Buchanan
6 Suyama
7 King
8 Callahan
9 Dodsworth
10 West
ShipperID ShipperName
1 Speedy Express
2 United Package
3 Federal Shipping
The below query returns ShipperID in descending order.
select ShipperID from Shippers order by ShipperID desc;
Now, I want to retrieve Names from the Employee table in the same order the ShipperID's are retrieved (3,2,1). My expected output is Leverling, Fuller, Davolio.
select Name from Employee where EmployeeID in (select ShipperID from Shippers order by ShipperID desc)
The above query is not returning the data as I expect. How to fix this?
UPDATE:
This is not about the ordering of records in ascending or descending order. This is just an example I have posted here. To make it more clear, assume that the subquery is returning ShipperID as 2,3,1. Now I want to retrieve the records from Employee table like Fuller, Leverling, Davolio

You will have to join the two tables if you want to keep the ordering. The order in the subquery is not preserved if you use IN

try this
select distinct Employee.Name from Employee
inner join Shippers on Shippers.ShipperID=Employee.EmployeeID
order by Shippers.ShipperID desc

Have you tried
select Name from Employee where EmployeeID in (select ShipperID from Shippers ) order by ShipperID desc

select Name from Employee where EmployeeID in (select ShipperID from Shippers ) order by EmployeeID desc
as employeeid=shipperid you can order by employeeid

If you want results to appear in a particular order, then you're going to have to pick something to order by. In your example, this means adding a column to define the order of the shippers and then joining that back to the employees table, so that you can then order the employee results accordingly.
Something like:
WITH employees AS (SELECT 1 employeeid, 'Davolio' NAME FROM dual UNION ALL
SELECT 2 employeeid, 'Fuller' NAME FROM dual UNION ALL
SELECT 3 employeeid, 'Leverling' NAME FROM dual UNION ALL
SELECT 4 employeeid, 'Peacock' NAME FROM dual UNION ALL
SELECT 5 employeeid, 'Buchanan' NAME FROM dual),
shippers AS (SELECT 1 shipperid, 'Speedy Express' shippername FROM dual UNION ALL
SELECT 2 shipperid, 'United Package' shippername FROM dual UNION ALL
SELECT 3 shipperid, 'Federal Shipping' shippername FROM dual)
-- end of mimicking your tables, see SQL below:
SELECT emp.employeeid,
emp.name
FROM employees emp
INNER JOIN (SELECT shipperid,
CASE WHEN shipperid = 1 THEN 3
WHEN shipperid = 2 THEN 1
WHEN shipperid = 3 THEN 2
END order_id
FROM shippers) shp ON emp.employeeid = shp.shipperid
ORDER BY shp.order_id ASC;
EMPLOYEEID NAME
---------- ---------
2 Fuller
3 Leverling
1 Davolio
Your order_id column may be something that already exists (e.g. a timestamp column) or generated (either by an explicit case statement as I demonstrated above, or by using the row_number() analytic function) but if you want your results to appear in a particular order, you need that column.

Related

How to correctly combine these two queries?

Employees table :
EmpID (Primary Key)
LastName
FirstName
Orders table :
OrderID (Primary Key)
CustID
EmpID
OrderDate
RequiredDate
ShippedDate
I need a query which returns EmpID, LastName, FirstName, total number of orders by employee, and total number of orders shipped late. An order is late if its ShippedDate is 5 days after RequiredDate. I got the late shipments by :
julianday(ShippedDate) - julianday(RequiredDate) >= 5
I thought to make two separate queries, one that takes a total count of the submissions and one for total count of late orders, then UNION them. I'm not getting the result I am looking for:
SELECT Employees.EmpId, Employees.LastName, Employees.FirstName, COUNT(*) as TotalCount, NULL
FROM Orders, Employees
WHERE Orders.EmpID = Employees.EmpID
GROUP BY LastName
UNION
SELECT Employees.EmpId, Employees.LastName, Employees.FirstName, NULL, COUNT(*) as LateCount
FROM Orders, Employees
WHERE Orders.EmpID = Employees.EmpID
AND julianday(ShippedDate) - julianday(RequiredDate) >= 5
GROUP BY LastName
I end up with offset null values on right side of the table :
TotalCount
NULL
17
NULL
NULL
25
etc.
What went wrong with my UNION? Why is the data offset and the column header wrong?
Is there a better approach?
"Is there a better approach?"
JOIN instead of UNION :
SELECT
Employees.EmpID,
Employees.LastName,
Employees.FirstName,
count(*) AS TotalCount,
sum(
julianday(Orders.ShippedDate) - julianday(Orders.RequiredDate) >= 5
) AS LateCount
FROM
Employees
JOIN Orders ON Orders.EmpID = Employees.EmpID
GROUP BY
Employees.EmpID
ORDER BY
TotalCount DESC,
LateCount DESC

Find department with maximum number of employees in Oracle SQL

I have these two tables and I need to find the department name with maximum number of employees.
The other solutions were not for Oracle, so I'm posting this question. Also, it would be really helpful if the query can be explained thoroughly as I'm finding it hard to visualise it.
EMPLOYEE
EMPNO EMPNAME MANAGER SALARY DEPT_NO
1 Puja 6 30000 2
2 Purabi 1 15000 3
3 Barun 6 23000 2
4 Sudha 1 20000 1
5 Amal 2 20000 1
6 Rakesh 3 30000 4
DEPARTMENT
Dept_No Dept_Name Location
1 Production LaneA
2 Marketing LaneB
3 Sales LaneC
4 HR LaneD
So far I could manage getting the highest number of employees. So I was thinking if somehow I can write another sub-query where I count the employees in the departments again and compare them to the max_num_emp that I calculated in the first query.
This is the query which retrieves the maximum number of employees. It does not return the dept_no.
select count(dept_no)
from employee
group by dept_no
order by count(dept_no) desc
fetch first row only;
Expected output
DEPT_NAME
Production
Marketing
I can also add the dept_no column in the query, then I will have to somehow find out how to get the max and that was somehow giving me errors because the query was violating some rules. I had actually tried doing max(above query).
So I thought of just getting the maximum employee count and then determine all departments which have those many employees and display their name.
You have a working query which you need to join to the table department:
select d.Dept_Name
from department d inner join (
select dept_no
from employee
group by dept_no
order by count(*) desc
fetch first row only
) t
on t.dept_no = d.dept_no
Edit
Try this (I cannot try it):
select d.dept_name
from department d inner join (
select x.dept_no from (
select dept_no, rank() over (order by count(*) desc) rn
from employee
group by dept_no
order by count(dept_no) desc
) x
where x.rn = 1
) t
on t.dept_no = d.dept_no
You may have used FETCH..FIRST syntax using WITH TIES instead of ONLY.
SELECT d.dept_name
FROM department d
JOIN employee e ON d.dept_no = e.dept_no
GROUP BY d.dept_name
ORDER BY COUNT(*)
DESC FETCH FIRST 1 ROW WITH TIES ;
Demo
If you are not looking for duplicates, then:
select d.dept_name, count(*)
from department d join
employee e
on d.dept_no = e.dept_no
group by d.dept_no, d.dept_name
order by count(dept_no) desc
fetch first row only;

Query returning records with duplicate data because of the wrong data in one of the columns

I have a record of an employee but my query is returning 2 records of this employee because the address column is different between the 2. How can solve this problem? Is it something that can be done? EMP_ID, CUS_LAST_NAME, CUS_FIRST_NAME, and GUARDIAN_ADDRESS are from 3 separate tables.
Example:
ID EMP_ID CUS_LAST_NAME CUS_FIRST_NAME GUARDIAN_ADDRESS
00000000 11111111 Jackson Michael 1111 Street Apt 1
ID EMP_ID CUS_LAST_NAME CUS_FIRST_NAME GUARDIAN_ADDRESS
00000000 11111111 Jackson Michael 1111 Street
if you can't the delete one of the two
if you don't matter which address the select return you can use an aggregation function for get one row only
select ID , EMP_ID , EMP_LAST_NAME, EMP_FIRST_NAME, min(ADDRESS)
from my_table
group by ID , EMP_ID , EMP_LAST_NAME, EMP_FIRST_NAME
If you want detect what employee have duplicates entries.
SELECT *
FROM employees
WHERE EMP_ID IN (
SELECT EMP_ID
FROM employees
GROUP BY EMP_ID
HAVING COUNT(*) > 1
)
--start with unique list of clients
SELECT DISTINCT a.ID, a.EMP_ID, e.EMP_LAST_NAME, e.EMP_FIRST_NAME, e.ADDRESS
FROM TABLE1 a
--attach on employee data on id
OUTER APPLY (SELECT TOP 1 b.EMP_LAST_NAME, b.EMP_FIRST_NAME, b.ADDRESS
FROM TABLE2 b
WHERE a.id = b.id
--use order by clause to change order and choose what top employee record u want to choose
ORDER BY b.address
) e
The quick and dirty way with max():
select id, emp_id, emp_last_name, emp_first_name, max(address) as address
from employees
group by id, emp_id, emp_last_name, emp_first_name
Alternative using: top with ties
select top 1 with ties
id, emp_id, emp_last_name, emp_first_name, address
from employees
order by row_number() over (partition by emp_id order by address desc)
rextester demo for both: http://rextester.com/EGGA75008

SQL select statement avoiding duplicated rows based on primary key

I have a table employee with two columns-empid(primary key), name. Suppose it has below three rows.
EmpID Name
---------------
11 Name1
12 Name2
11 Name3
How would I write a select statement to select records avoiding the two rows which have duplicating empid. I used query like:
select empid, name
from(select empid, name, row_number() over(partition by empid order by empid desc) rnk
from t)a
where a.rnk=1
But this query will give
EmpID Name
---------------
11 Name1
12 Name2
As the result. But all I need is
EmpID Name
---------------
12 Name2
try this query, this will work and give you the row 12 Name2
select empid, name from employee a
join (
select empid , count(empid) as count1 from employee
group by empid
having count(empid)=1 ) b on a.empid=b.empid
select empid, name
from(select empid, name,count(*) over(partition by empid) cnt from t) t
where cnt=1
An anti join using NOT EXISTS might be the fastest approach:
SELECT empID, Name
FROM T
WHERE NOT EXISTS (SELECT 1 FROM T AS T2 WHERE T2.EmpID = T.EmpID AND T2.Name <> T.Name);
I have done no testing, so it is possible that the optimiser might be able to generate a anti semi-join using a count = 1 operation, but this gives it the best possible chance of getting to that plan.
Would not SELECT max(empid) as empid, name from employee group by name having count(distinct empid) < 2 work?

Oracle Grouping and Duplicate Checking

I've been scratching my head on this one.
This question got me closer but my situation is a little more complex.
How do I find duplicate values in a table in Oracle?
Suppose I have a table EMPLOYEE and I want to know which employees work in multiple departments. My table has the employee id, and department they work in. When an employee works in multiple departments, their employee id will be listed as multiple records. I don't want to just count the employees that are listed twice. I need to know how many work in this list of departments vs this list of departments.
So for example if my table is:
Employee ID | Department
1 Accounting
1 Marketing
2 Accounting
3 Finance
4 Programming
And Department List A is
Accounting
Finance
And Department List B is
Marketing
Programming
Then the results of the query would be
Employee ID | Department
1 Accounting
1 Marketing
or
Employee ID | Count(Department)
1 2
since employee 1 works in a department from List A and List B.
I would approach this with a mapping:
with mapping as (
select 'Accounting' as department, 'A' as grouping from dual union all
select 'Finance' as department, 'A' as grouping from dual union all
select 'Marketing' as department, 'B' as grouping from dual union all
select 'Programming' as department, 'B' as grouping from dual
)
select employeeid, count(distinct m.grouping)
from t join
mapping m
on t.department = m.department
group by employeeid;
Note: you may want to store this information in another table or as an attribute in the Departments table.
If you want solo departments to map to themselves:
select t.employeeid, count(distinct coalesce(m.grouping, t.department))
from t join
mapping m
on t.department = m.department
group by t.employeeid;
If you want other departments not in the table to be grouped together:
select t.employeeid, count(distinct coalesce(m.grouping, 'UNKNOWN MAPPING'))
from t join
mapping m
on t.department = m.department
group by t.employeeid;
If I understand what you're after, you can use conditional aggregation in a HAVING clause for this:
SELECT Employee_ID, COUNT(DISTINCT Department) AS Dept_Count
FROM YourTable
GROUP BY Employee_ID
HAVING MAX(CASE WHEN Department IN ('Accounting','Finance') THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN Department IN ('Marketing','Programming') THEN 1 ELSE 0 END) = 1