Case or If, What to choose in SQL - sql

How can i manage this query. I want to have only one person in the query. If there is an id in empId column, then the name of him. Otherwise the name of the boss.
Product empId teamId
----------------------
A 1 3
B 2 4
C 3
D 2 3
E 4
User Table
Id Name
-----------
1 Jim
2 Carrey
3 Bill
4 Clinton
Team Table
Team_Id BossId
-----------
3 3
4 4
The result should look like:
Product user.name
-----------------
A Jim
B Carrey
C Bill
D Carrey
E Clinton

Use CASE or the COALESCE() function:
SELECT
x.Product
, COALESCE(emp.Name, boss.Name) AS Name
FROM
TableX AS x
LEFT JOIN
User AS emp
ON emp.Id = x.empId
LEFT JOIN
User AS boss
ON boss.Id = x.bossId
Updated:
SELECT
x.Product
, COALESCE(emp.Name, boss.Name) AS Name
FROM
TableX AS x
LEFT JOIN
User AS emp
ON emp.Id = x.empId
LEFT JOIN
Team As t
JOIN
User AS boss
ON boss.Id = t.bossId
ON t.team_Id = x.teamId

Something like this:
SELECT
Table1.Product,
CASE
WHEN Table1.empId IS NULL
THEN Boss.name
ELSE Emp.name
END
FROM
Table1
LEFT JOIN [User] AS Emp
ON Emp.Id =Table1.empId
LEFT JOIN Team
ON Team.Team_Id =Table1.teamId
LEFT JOIN [User] AS Boss
ON Boss.Id=Team.BossId

SELECT Y.Product, U.Name
FROM YourTable AS Y
JOIN Users AS U ON Y.empId = U.Id
UNION
SELECT Y.Product, U.Name
FROM YourTable AS Y
JOIN Team AS T ON Y.teamId = T.Team_Id
JOIN Users AS U ON T.BossId = U.Id
WHERE Y.empId IS NULL;

-- SET search_path='tmp';
DROP TABLE tmp.products;
CREATE TABLE products
( product CHAR(1)
, emp_id INTEGER
, team_id INTEGER
);
INSERT INTO products(product,emp_id,team_id)
VALUES ('A',1,3), ('B',2,4), ('C',NULL,3), ('D',2,3), ('E',NULL,4);
DROP TABLE tmp.names;
CREATE TABLE names
(id INTEGER
, zname varchar
);
INSERT INTO names(id,zname)
VALUES ( 1, 'Jim') ,( 2, 'Carrey') ,( 3, 'Bill') ,( 4, 'Clinton') ;
DROP TABLE tmp.teams;
CREATE TABLE teams
( team_id INTEGER NOT NULL
, boss_id INTEGER NOT NULL
);
INSERT INTO teams(team_id,boss_id) VALUES ( 3,4) , (4,4);
WITH lutser(prod,id,team) AS
(
SELECT k1.product AS prod
, k1.emp_id AS id
, k1.team_id AS team
FROM tmp.products k1
UNION
SELECT k2.product AS prod
, t.boss_id AS id
, k2.team_id AS team
FROM tmp.products k2
JOIN tmp.teams t ON t.team_id = k2.team_id
WHERE k2.emp_id IS NULL
)
SELECT l.prod
, l.id
, l.team
, n.zname
FROM lutser l
JOIN names n ON n.id = l.id
;
extra bonus point for a recursive version of this CTE ...

Related

Query to get all siblings

I have table like that:
ID NAME SURNAME MotherID FatherID
0 JJ smi NULL NULL
1 ja rly NULL NUL
2 ak smi 0 1
3 ol smi 0 1
4 oa woo 2 3
5 oe boo 2 3
etc.
I need query that with specific parameter as NAME and surname will return me all siblings of a person.
Expected output
NAME SURNAME FATHERNAME FATHERSURNAME MOTHERNAME MOTHERSURNAME
AK SMI JA RLY JJ SMI
OL SMI JA RLY JJ SMI
I tried
SELECT
a.name,
a.surname
FROM PEOPLE a, PEOPLE b
WHERE (b.name = 'ak' AND b.surname ='smi' AND
(b.motherID = a.ID OR b.fatherid = ID))
You can use a SUB QUERY to achieve this.
Table Creation:
DECLARE #T TABLE(
ID INT,
NAME VARCHAR(MAX),
SURNAME VARCHAR(MAX),
MOTHERID INT,
FATHERID INT)
Table Insertion:
Insert into #t Values(0,'JJ','smi',NULL,NULL)
Insert into #t Values(1,'ja','rly',NULL,NULl)
Insert into #t Values(2,'ak','smi',0,1)
Insert into #t Values(3,'ol','smi',0,1)
Insert into #t Values(4,'oa','woo',2,3)
Insert into #t Values(5,'oe','boo',2,3)
Query:
SELECT S.NAME AS NAME,S.SURNAME AS SURNAME,S.FATHER_NAME ,S.FATHER_SURNAME,M.NAME AS
MOTHER_NAME,M.SURNAME AS MOTHER_SURNAME
FROM #T M INNER JOIN(
SELECT T2.NAME AS NAME,T2.SURNAME AS SURNAME,T1.NAME AS FATHER_NAME,T1.SURNAME AS
FATHER_SURNAME,T2.MOTHERID
FROM #T T1
INNER JOIN #T T2 ON T1.ID=T2.FATHERID
WHERE T2.NAME IN ('AK','OL'))S ON M.ID=S.MOTHERID
Output:
Also ,add the WHERE clause based on your parameter requirement.
Here is one way you can get it. From here you can explore a few other options!
CREATE PROC GetSiblings
(
#GivenName AS VARCHAR(100),
#SurName AS VARCHAR(100)
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
A.GivenName,
A.SurName,
F.GivenName AS FatherGivenName,
F.SurName AS FatherSurName,
M.GivenName AS MotherGivenName,
M.SurName AS MotherSurName,
FROM PEOPLE AS P INNER JOIN PEOPLE AS F ON P.FatherID = F.ID
INNER JOIN PEOPLE AS M ON P.MotherID = M.ID
WHERE P.GivenName = #GivenName AND P.SurName = #SurName
UNION --By father
SELECT
A.GivenName,
A.SurName,
F.GivenName AS FatherGivenName,
F.SurName AS FatherSurName,
M.GivenName AS MotherGivenName,
M.SurName AS MotherSurName,
FROM PEOPLE AS P INNER JOIN PEOPLE AS F ON P.FatherID = F.ID
INNER JOIN PEOPLE AS M ON P.MotherID = M.ID
WHERE P.FatherID IN (SELECT DISTINCT FatherID FROM PEOPLE WHERE GivenName = #GivenName AND SurName = #SurName)
UNION --By mother
SELECT
A.GivenName,
A.SurName,
F.GivenName AS FatherGivenName,
F.SurName AS FatherSurName,
M.GivenName AS MotherGivenName,
M.SurName AS MotherSurName,
FROM PEOPLE AS P INNER JOIN PEOPLE AS F ON P.FatherID = F.ID
INNER JOIN PEOPLE AS M ON P.MotherID = M.ID
WHERE P.MotherID IN (SELECT DISTINCT MotherID FROM PEOPLE WHERE GivenName = #GivenName AND SurName = #SurName);
END;

Return two relationship in one resultset

Below are the oversimplified inspired from my production schema.
Thus, I want to return all these information in one result set.
My desired result set
I have tried this query but it return wrong result.
SELECT u.*
,f.*
,uv.*
,v.*
FROM User u
LEFT JOIN UserFarm uf ON uf.UserID = u.ID
LEFT JOIN Farm f ON f.ID = uf.FarmID
LEFT JOIN FarmVehicle fv ON f.ID = fv.FarmID
LEFT JOIN UserVehicle uv ON u.ID = uv.UserID
LEFT JOIN Vehicle v ON fv.VehicleID = v.ID
WHERE u.ID = 1
Edit: This is the result from above query.
Could anyone advise me on this, I really need to return in the desired result.
You could build a query to return the data you want in the first row, and null in the columns you don't want for that row. Then create another query for the second row and use union all to combine the data together:
declare #UserVehicle table (UserID int, VehicleID int, IsService bit)
declare #User table (ID int, Name varchar(10))
declare #UserFarm table (UserID int, FarmID int)
declare #Farm table (ID int, Name varchar(10))
declare #FarmVehicle table (FarmID int, VehicleID int)
declare #Vehicle table (ID int, [Type] varchar(10), Name varchar(10))
insert into #UserVehicle values (1, 2, 1)
insert into #User values (1, 'Sam')
insert into #UserFarm values (1, 1)
insert into #Farm values (1, 'Flora')
insert into #FarmVehicle values (1, 1)
insert into #Vehicle values (1, 'Larry', 'Scania'), (2, 'Unknown', 'Civic')
select [User.ID] = u.ID
,[User.Name] = u.Name
,[Farm.ID] = f.ID
,[Farm.Name] = f.Name
,[UserVehicle.UserID] = null
,[UserVehicle.VehicleID] = null
,[UserVehicle.IsService] = null
,[Vehicle.ID] = v.ID
,[Vehicle.Name] = v.Name
from #User u
left join #UserFarm uf on u.ID = uf.UserID
left join #Farm f on uf.FarmID = f.ID
left join #FarmVehicle fv on f.ID = fv.VehicleID
left join #Vehicle v on fv.VehicleID = v.ID
union all
select [User.ID] = u.ID
,[User.Name] = u.Name
,[Farm.ID] = null
,[Farm.Name] = null
,[UserVehicle.UserID] = uv.UserID
,[UserVehicle.VehicleID] = uv.VehicleID
,[UserVehicle.IsService] = case when uv.IsService = 0 then 'No' else 'Yes' end
,[Vehicle.ID] = v.ID
,[Vehicle.Name] = v.Name
from #User u
left join #UserFarm uf on u.ID = uf.UserID
left join #UserVehicle uv on u.ID = uv.UserID
left join #Vehicle v on uv.VehicleID = v.ID
This query returns the following dataset:
User.ID User.Name Farm.ID Farm.Name UserVehicle.UserID UserVehicle.VehicleID UserVehicle.IsService Vehicle.ID Vehicle.Name
----------- ---------- ----------- ---------- ------------------ --------------------- --------------------- ----------- ------------
1 Sam 1 Flora NULL NULL NULL 1 Scania
1 Sam NULL NULL 1 2 Yes 2 Civic
If you do not want to see users who are not assigned to vehicles or farms, then change all left join to inner join.
Try with following query
SELECT U.*,T.* FROM (
SELECT F.FARMID,F.NAME AS NAM
,UV.*
,V.*
FROM VEHICLE V
LEFT JOIN FARMVEHICLE FV ON FV.VEHICLEID = V.ID
LEFT JOIN USERVEHICLE UV ON UV.VEHICLEID = V.ID
LEFT JOIN FARM F ON F.FARMID = FV.FARMID
LEFT JOIN USERFARM UF ON UF.FARMID = F.FARMID)T, USER U
Let me know if any issue in query.
You want to show a user's farms and vehicles. However farms and vehicles can be related, and in that case you want to show them together in a row rather than in separated rows.
Example: User1 is related to Farm1, Farm2, and Farm3 and to Vehicle1, Vehicle2, and Vehicle3. Moreover, Farm1 is related to Vehicle1 and Vehicle2 and Farm2 is also related to Vehicle1.
Then you want:
user | farm | vehicle
------+-------+---------
User1 | Farm1 | Vehicle1
User1 | Farm1 | Vehicle2
User1 | Farm2 | Vehicle1
User1 | Farm3 | -
User1 | - | Vehicle3
So the second and third columns show the relations of farms with vehicles. You get these with a full outer join. The join's ON clause is a bit tricky. You want the userid to match and the combination of farmid and vehicleid to be found in the bridge table farmvehicle. Here is one way to do this:
with rel as
(
select coalesce(uf.userid, uv.userid) as userid, uf.farmid, uv.vehicleid
from userfarm uf
full outer join uservehicle uv
on uf.userid = uv.userid
and exists (select * from farmvehicle fv where fv.farmid = uf.farmid
and fv.vehicleid = uv.vehicleid)
)
select u.name as user_name, f.name as farm_name, v.name as vehicle_name
from usr u
left join rel on u.id = rel.userid
left join farm f on f.id = rel.farmid
left join vehicle v on v.id = rel.vehicleid
order by user_name, farm_name, vehicle_name;
Rextester demo: http://rextester.com/DNKSU25931

Update a column separated by comma for multiple values?

I have following table Userinfo_attarea:
id employee_id area_id
182521 4391 2
182522 4391 3
Personnel_area:
id areaid
1 Area Name
2 PROJECT80
3 PROJECT69
When i update it works fine foe single value but i need a column with multiple values separeted by comma as mentioned below
Expected Output:
areaname
PROJECT80,PROJECT69
i am using following query for update
UPDATE employee
SET employee.areaname = p.areaname
FROM employee join userinfo u
on u.badgenumber=employee.emp_reader_id join userinfo_attarea ua on ua.employee_id=u.userid join personnel_area p on ua.area_id=p.id
JOIN inserted I ON u.userid= I.employee_id
Thanks in advance...
You can try this :
Update E set areaname = T.areaname from #Employee E
Inner Join
(
Select employee_id,STUFF((SELECT ', ' + CAST(areaid AS VARCHAR(10)) [text()]
FROM (
Select U.employee_id,P.areaid from #Userinfo_attarea U
Inner Join #Personnel_area P on U.area_id = P.id
) B
WHERE employee_id = A.employee_id
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') areaname from
(
Select U.employee_id,P.areaid from #Userinfo_attarea U
Inner Join #Personnel_area P on U.area_id = P.id
) A
GROUP BY employee_id
) T on T.employee_id = E.id
Working demo

count after join on multiple tables and count of multiple column values

Please help me with below problem.
table 1 employee details
emp name empno.
---------------------------------
John 1234
Joe 6789
table 2 employee assignment
empno assignmentstartdate assignmentenddate assignmentID empassignmentID
-----------------------------------------------------------------------------
1234 01JAN2017 02JAN2017 A1 X1
6789 01jan2017 02JAN2017 B1 Z1
table 3 employee assignment property
empassignmentID assignmentID propertyname propertyvalue
-------------------------------------------------------------------
X1 A1 COMPLETED true
X1 A1 STARTED true
Z1 B1 STARTED true
Z1 B1 COMPLETED false
Result wanted: (count of completed and started for each employee)
emp name emp no. COMPLETED STARTED
------------------------------------------
John 1234 1 1
Joe 6789 0 1
Currently with my query it is not putting count correctly for propertyvalue if I run for one employee it works correctly but not for multiple employees.
Please help.
SELECT empno ,
empname ,
(SELECT COUNT(A.propertyvalue)
FROM employeedetails C ,
employees_ASSIGNMENT RCA,
employee_assignment_property A
WHERE TRUNC(startdate) >= '14jun2017'
AND TRUNC(endate) <= '20jun2017'
AND RCA.empno = C.empno
AND RCA.empassignmetid = A.empassignmetid
AND rca.EMPNO IN ('1234','6789')
AND RCA.assignmentid = A.assignmentid
AND A.Name = 'COMPLETED'
AND A.propertyvalue = 'true') ,
(SELECT COUNT(A.propertyvalue)
FROM employeedetails C ,
employees_ASSIGNMENT RCA,
employee_assignment_property A
WHERE TRUNC(startdate) >= '14jun2017'
AND TRUNC(endate) <= '20jun2017'
AND RCA.empno = C.empno
AND RCA.empassignmetid = A.empassignmetid
AND rca.EMPNO IN ('1234','6789')
AND RCA.assignmentid = A.assignmentid
AND A.Name = 'STARTED'
AND A.propertyvalue = 'true')FROM employeedetails WHERE EMPNO IN
('1234','6789') GROUP BY C.empno ,
C.EMPNAME
I think you are simply looking for this:
SELECT DET.empname
, COUNT(CASE WHEN PROP.propertyname = 'COMPLETED' THEN 1 END) COMP_COUNT
, COUNT(CASE WHEN PROP.propertyname = 'STARTED' THEN 1 END) START_COUNT
FROM employeedetails DET
INNER JOIN employees_ASSIGNMENT ASS
ON ASS.empno = DET.empno
INNER JOIN employee_assignment_property PROP
ON PROP.empassignmentID = ASS.empassignmentID
AND PROP.assignmentID = ASS.assignmentID
GROUP BY DET.empname
Just add a WHERE clause if you need one.
if you want you result as a query without CTEs this should work:
select empName,
empNo,
(select employee_details.empNo, count(employee_assignment.assId)
from employee_details as t1
join employee_assignment on (t1.empno = employee_assignment.empno)
join employee_assignment_property on (employee_assignment.assId = employee_assignment_property.assId)
where employee_assignment.ptop = 'COMPLETED'
and t.empNo = t1.empNo
group by t1.empNo ) as [COMPLETED],
(select employee_details.empNo, count(employee_assignment.assId)
from employee_details as t1
join employee_assignment on (t1.empno = employee_assignment.empno)
join employee_assignment_property on (employee_assignment.assId = employee_assignment_property.assId)
where employee_assignment.ptop = 'STARTED'
and t.empNo = t1.empNo
group by t1.empNo ) as [STARTED],
from employee_details as t
If you don't want to do a dirty query composed of subqueries, you can try creating a view (if your database permits it).
What does it mean : I'll be useless in front of this. In summary, a view is a temporary table.
Hope this helps
this should work using CTEs:
Using Common Table Expressions
with numComplet()
as
(
select tbl1.empNo, count(tbl2.assId)
from tbl1
join tbl2 on (tbl1.empno = tbl2.empno)
join tbl3 on (tbl2.assId = tbl3.assId)
where tbl2.ptop = 'COMPLETED'
group by tbl1.empNo
),
with numStarted()
as
(
select tbl1.empNo, count(tbl2.assId)
from tbl1
join tbl2 on (tbl1.empno = tbl2.empno)
join tbl3 on (tbl2.assId = tbl3.assId)
where tbl2.ptop = 'STARTED'
group by tbl1.empNo
)
select *
from tbl1
join numComplet on (tbl1.empNo = numComplet.empNo)
join numStarted on (tbl1.empNo = numStarted.empNo)
I put down table names as tbl[1|2|3]

Unable to JOIN and get the final query

I'm having this following situation for which i'm unable to form a sql query. Please help me with this.
create table student(
studentName varchar(50)
);
Insert into student values('abc');
Insert into student values('mnop');
Insert into student values('xyz');
Insert into student values('pqrs');
create table workAssigned (
topic varchar(50),
creator varchar(50),
reviewer varchar(50),
creationCount decimal(4,2)
reviewCount decimal(4,2)
);
insert into workAssigned values('algebra','abc','mnop',1.25,0.75);
insert into workAssigned values('geometry','mnop','xyz',1.5,0.25);
insert into workAssigned values('algorithms','mnop','xyz',1.5,1.5);
insert into workAssigned values('derivative','xyz','abc',0.25,1);
final output reqiured is:
StudentName NumberOfWorkDone Effort
abc 2 2.25
mnop 3 3.75
xyz 3 2
pqrs NUll NULL
Where NumberOfWorkDone is SUM of creator and Reviewer, and
Effort is SUM of creationCount and reviewCount
You can try using Group By, Join and CASE with Aggregate functions,
SELECT studentName,
SUM(CASE WHEN W.creator = S.studentName OR S.studentName = W.reviewer THEN 1 ELSE NULL END ) AS NumberOfWorkDone,
SUM(CASE WHEN W.creator = S.studentName THEN W.creationCount WHEN S.studentName = W.reviewer THEN W.REVIEWCOUNT ELSE NULL END) AS Efforts
FROM student S
LEFT JOIN workAssigned W
ON S.studentName = W.creator OR S.studentName = W.reviewer
GROUP BY studentName
I have added NULL into Case statement for else part as your output was showing NULL but I think it would be better if you would replace that with 0.
Select studentName,NumberOfWorkDone,Effort
From student as A
Left Join
(
Select creator,count(creator) as NumberOfWorkDone,sum(creationCount) as Effort
From
(
Select creator,creationCount
from workAssigned as A
Union all
Select reviewer,reviewCount from workAssigned
)as A
Group by creator
)as B
On A.studentName=B.creator
select * from
(select count(col_X) as F, col_X
from
(select creator as col_X from workAssigned
UNION ALL
select reviewer as col_X from workAssigned) AS A
group by col_X) as E
left join
(select A, sum(B)
from
(select creator as A, creationCount as B
from workAssigned
UNION ALL
select revieweras A, reviewCount as B
from workAssigned) as C
group by A) as D
on E.F = D.A