Update using joins by finding the previous value in sql - sql

I have two tables and I have to update the address field in the emp table by looking up in the emphistory table with previous value i.e USA for employee John
Table emp
EId ename sal Address AccountId
-------------------------------
101 John 100 U X12
102 Peter 500 Null X13
Table emphistory
emphisid EId AccountId Address Date (use row_number to find the second record for that eid and accountid)
-----------------------------------------------------
1 101 X12 U 11-01-2020 09:45:00
2 102 X13 Null 11-01-2020 09:46:00
3. 101 X12 USA 11-01-2020 09:30:00
I have to join the tables with account id and eid.

This works in Postgresql & Sql Server
UPDATE emp
SET Address = hist.Address
FROM (
SELECT h.EId, h.AccountId, h.Address
, ROW_NUMBER() OVER (PARTITION BY h.EId, h.AccountId
ORDER BY h.Date DESC) AS Rn
FROM emphistory h
JOIN emp e
ON e.EId = h.EId
AND e.AccountId = h.AccountId
WHERE h.Address IS NOT NULL
) hist
WHERE emp.EId = hist.EId
AND hist.Rn = 2

Related

Updating the first occurrence of the column with a value and the remaining with other value

select emp_id, emp_dept, emp_name
from employee
where emp_id in (123, 234);
emp_id emp_dept emp_name
*****************************
123 222 1234
123 222 5678
123 222 9101
234 222 1011
234 222 1112
234 222 1213
Here there are 3 records for each emp_id.
I want a query to update the emp_dept such that out of three records, only one record will be updated to 555(it can be any record doesnt matter) and the other 2 will be updated to 666.
Create a CTE (common table expression) adding a ROW_NUMBER window function partitioned by emp_id then write an update statement joining the cte and building a case statement to determine row number
The code below builds a Table Variable with Test data, selects the data to show you the "before" and then modifies with the cte method and selects the data to show you the final result.
;WITH cte AS (
SELECT
emp_id
,ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY emp_SSN) AS RowNum
FROM
#Table
)
UPDATE t
SET emp_dept = CASE WHEN RowNum = 1 THEN 555 ELSE 666 END
FROM
#Table t
INNER JOIN cte u
ON t.emp_id = u.emp_id
You can use MERGE.
Data Preparation
create table em1(
emp_id number, emp_dept number, emp_name varchar2(10));
insert into em1 values(123,1,'we');
insert into em1 values(123,1,'asd');
insert into em1 values(123,1,'rfw');
insert into em1 values(345,2,'rtg');
insert into em1 values(345,2,'bfg');
insert into em1 values(345,2,'uyi');
commit;
Query
MERGE INTO em1 e
USING (
SELECT emp_id, emp_dept, emp_name,
row_number() over (partition by emp_id order by 1) r
FROM em1
WHERE emp_id in (123,345)
) f
ON (f.emp_id = e.emp_id and f.emp_name = e.emp_name)
WHEN MATCHED THEN
UPDATE SET e.emp_dept = case when f.r = 1 then 555 else 666 end;
Result
emp_id emp_dept emp_name
-------------------------
123 555 we
123 666 asd
123 666 rfw
345 555 rtg
345 666 bfg
345 666 uyi

CTE to replace JOIN

Can I use CTE instead of the my code here ? Here are the two tables and my code .
tbl1
D_ID department employee name manager name
112 d01 john d Peter k
113 d01 Emily f kevin s
tbl2
Emp_id employee name D_ID
100 john d 112
200 peter k 112
800 Emily f 113
500 kevin s 113
My code below , what I did is I joined tbl1 with tbl2 on D_ID and employee name and then filter out the records where employee's emp_id > manager's emp_id .
DECLARE #level nvarchar(MAX) =
(
select X.D_ID ,x.employee_NAME, x.emp_ID as employee_id,
y.manager_name ,y.emp_id as manager_id
) + ' '
from (
select distinct b.d_id , a.emp_id as employee_id
from tbl1 a , tbl2 b
where a.d_id=b.d_id and a.employee_NAME=b.employee_NAME
) x ,
(
select distinct b.d_id , a.emp_id as manager_id
from tbl1 a , tbl2 b
where a.d_id=b.d_id and a.employee_NAME=b.manager_NAME
) Y
where x.department=y.department and x.employee_id>=y.manager_id
FOR XML PATH('')
)
IF #level IS NOT NULL
BEGIN
RAISERROR(' employee ID>manager_id: %s',16, 1, #level)
with log;
END;
DESIRED OUTPUT is below as Emily f's employee id is > than her manager's ID .
D_ID employee_NAME employee_id manager_name manager_id
113 Emily f 800 kevin s 500
I think you just need some joins:
select t1.d_id, t1.employee_name, te.emp_id,
t1.employee_name as manager_name, tm.emp_id as manager_id
from tbl1 t1 join
tbl2 te
on t1.employee_name = te.employee_name join
tbl2 tm
on t1.manager_name = tm.employee_name
where te.emp_id > tm.emp_id;
It is quite curious that you are using the names to connect the two tables. Normally, you would use the employee id for this purpose and look up the name using the id.

Select highest value over multiple tables

I have the following tables
Table Name : tHREmployee
EmployeID# Employee DepartmentID DesignationID DOB BasicPay
101 Ajith 101 102 10/Feb/1982 10000
102 Aarathy NULL 101 15/Mar/1981 15000
103 Aruna 102 NULL 20/Sep/1980 5000
104 Ambily 101 NULL 20/Sep/1980 5000
105 Anjaly NULL 101 20/Sep/1980 10000
106 Babitha 103 NULL 20/Sep/1981 20000
Table Name : tHRDepartment
DepartmentID# Code Department
101 500 Production
102 501 HR
103 502 Finance
105 503 Marketing
Table Name : tHRDesignation
DesignationID# Designation
101 Executive
102 Manager
How can I display employee details (Employee, Department, Designation, basicPay) for those employees who have highest basic pay for each department in sql?
In Oracle, this should help you
WITH CTE AS
(
SELECT DepartmentID , MAX(BasicPay) MAXPAY FROM tHREmployee
GROUP BY DepartmentID
)
SELECT E.EmployeID EMPid, E.Employee EMP_NAME , DPT.Department DPT_NAME,
DSG.Designation DSG_NAME, BASICPAY
FROM CTE, tHREmployee E,tHRDepartment DPT, Designation DSG
WHERE
E.BASICPAY = MAXPAY
AND E.DepartmentID = CTE.DepartmentID
AND E.DepartmentID = DPT.DepartmentID
AND E.DesignationID = DSG.DesignationID
In other DBs, something similar to this would help you:
SELECT E.EmployeID EMPid, E.Employee EMP_NAME , DPT.Department DPT_NAME,
DSG.Designation DSG_NAME, BASICPAY
FROM tHREmployee E,tHRDepartment DPT, Designation DSG
WHERE
E.DepartmentID = CTE.DepartmentID
AND E.DepartmentID = DPT.DepartmentID
AND E.DesignationID = DSG.DesignationID
AND E.BASICPAY IN
( SELECT MAX(EE.BASICPAY) FROM tHREmployee EE GROUP BY DPT.DepartmentID )
Select A.Employee,B.Department ,c.Designation,a.BAsicPay from Employee A inner join Department B on A.Department=B.DepartmentID left join Designation C on A.designationID=C.DesignationID inner join (Select DepartmentID,max(BasicPay) as BasicPay from Employee group by DepartmentID) as s2
on s2.DepartmentID=A.DepartmentID and A.BAsicPay=s2.BasicPAy

Return multiple values in one column

I have two tables
Employee:
Empid Ename Eage Eadd Ephone
1 x 23 b 677
2 y 24 h 809
3 z 34 u 799
Department:
Did fkEmpid dname ddescription
123 1 test test
234 1 test1 test1
667 2 hello hello
Finally I want something like this
Ename Eage Eadd Ephone dname
x 23 b 677 test,test1
y 24 h 809 hello
z 34 u 799 null
Please help me with the SQL
It certainly would be nice to know the target RDBMS. But this question is asked so often so let's try and list'em all (at least popular ones) side by side.
For SQL Server:
SELECT e.Ename, e.Eage, e.Eadd, e.Ephone, d.dname
FROM Employee e LEFT JOIN
(
SELECT fkEmpid,
STUFF((SELECT ',' + dname
FROM Department
WHERE fkEmpid = t.fkEmpid
FOR XML PATH('')) , 1 , 1 , '' ) dname
FROM Department t
GROUP BY fkEmpid
) d
ON e.Empid = d.fkEmpid
Here is SQLFiddle demo
For Mysql, SQLite, HSQLDB 2.X:
SELECT e.Ename, e.Eage, e.Eadd, e.Ephone, d.dname
FROM Employee e LEFT JOIN
(
SELECT fkEmpid,
GROUP_CONCAT(dname) dname
FROM Department t
GROUP BY fkEmpid
) d
ON e.Empid = d.fkEmpid
Here is SQLFiddle demo (MySql)
Here is SQLFiddle demo (SQLite)
For Oracle 11g:
SELECT e.Ename, e.Eage, e.Eadd, e.Ephone, d.dname
FROM Employee e LEFT JOIN
(
SELECT fkEmpid,
LISTAGG (dname, ',') WITHIN GROUP (ORDER BY dname) dname
FROM Department t
GROUP BY fkEmpid
) d
ON e.Empid = d.fkEmpid
Here is SQLFiddle demo
For PostgreSQL 9.X:
SELECT e.Ename, e.Eage, e.Eadd, e.Ephone, d.dname
FROM Employee e LEFT JOIN
(
SELECT fkEmpid,
string_agg(dname, ',') dname
FROM Department t
GROUP BY fkEmpid
) d
ON e.Empid = d.fkEmpid
Here is SQLFiddle demo
Output in all cases:
| ENAME | EAGE | EADD | EPHONE | DNAME |
---------------------------------------------
| x | 23 | b | 677 | test,test1 |
| y | 24 | h | 809 | hello |
| z | 34 | u | 799 | (null) |
Considering RDBMS as SQL SERVER 2008
select E.Ename,E.Eage,E.Eadd,E.Ephone,D.dname
into Table1
from Employee E
left join Deparment D on E.Empid=D.fkEmpid
select t1.[Ename], t1.[Eage], t1.[Eadd], t1.[Ephone],
STUFF((
SELECT ', ' + t2.dname
FROM Table1 t2
WHERE t2.Ename = t1.Ename
AND t2.Eage=t1.Eage
AND t2.Eadd=t1.Eadd
AND t2.Ephone=t1.Ephone
FOR XML PATH (''))
,1,2,'') AS Names
FROM Table1 t1
GROUP BY t1.Ename,t1.[Eage], t1.[Eadd], t1.[Ephone];
SQL FIDDLE

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)