SQL-Using JOINS to get the values from three tables - sql

I have used four tables, namely VehicleMaster, OwnerMaster, CustomerMaster and CustomerVehicle in my project
As name explains, first three are master tables and last table (CustomerVehicle) contains CustomerID & VehicleID. The table hierarchy are as follows
OwnerMaster (OwnerID, OwnerName)
VehicleMaster (VehicleID, OwnerID, VehicleDetails blah blah...)
CustomerMaster (CustomerID, CustomerName..)
CustomerVehicle (CustomerID, VehicleID)
Now i would like to get how many vehicles are running under each owner. output should be something like this.
OwnerName, TotalVehicles, No of Running Vehicles, NonRunning Vehicles.
xxxx, 40, 34, 6
Any help will be much appreciated.
Thanks,

Something like this should work; left join the tables and (distinct) count the respective fields;
SELECT om.OwnerName,
COUNT(DISTINCT vm.VehicleID) TotalVehicles,
COUNT(DISTINCT cv.VehicleID) Running,
COUNT(DISTINCT vm.VehicleID)-COUNT(DISTINCT cv.VehicleID) NotRunning
FROM OwnerMaster om
LEFT JOIN VehicleMaster vm ON om.OwnerID = vm.OwnerID
LEFT JOIN CustomerVehicle cv ON vm.VehicleID = cv.VehicleID
GROUP BY om.OwnerID, om.OwnerName
An SQLfiddle to test with.

There can be owners without vehicles, we don't know... something like this should work as well:
SELECT
om.OwnerName,
count(vm.VehicleID) Total,
sum(case when vm.VehicleID is not null
and cv.VehicleID is not null then 1 else 0 end) Runnng,
sum(case when vm.VehicleID is not null
and cv.VehicleID is null then 1 else 0 end) NotRunnng
FROM OwnerMaster om
LEFT JOIN VehicleMaster vm ON om.OwnerID = vm.OwnerID
LEFT JOIN CustomerVehicle cv ON vm.VehicleID = cv.VehicleID
GROUP BY om.OwnerName

Thank you very much Elhana. I have updated my query to get the exact result what i need. Modified query as:
SELECT
om.OwnerName,
count(vm.VehicleID) Total,
sum(case when vm.VehicleID is not null
and cv.VehicleID is not null then 1 else 0 end) Runnng,
sum(case when vm.VehicleID is not null
and cv.VehicleID is null then 1 else 0 end) NotRunnng
FROM OwnerMaster om
LEFT JOIN VehicleMaster vm ON om.OwnerID = vm.OwnerID and vm.isactive = 1
LEFT JOIN (select distinct VehicleID from CustomerVehicle where isactive = 1) cv
ON vm.VehicleID = cv.VehicleID
where om.isactive = 1
GROUP BY om.OwnerName
SQL Fiddle

Related

Convert multiple rows to columns but as one rows

How do i achieve achieve all this records under one row since every employee has one of each NHIF, NSSF and KRA number?
Below is the query i used but the all appear separate rows.
SELECT DISTINCT
hrmemployeehdr.employeeslno,
hrmemployeehdr.employeecode,
hrmemployeehdr.employeefirstname,
hrmemployeehdr.employeemiddlename,
hrmemployeehdr.employeelastname,
hrmDesignationHdr.DesignationName,
hrmemployeehdr.DateOfBirth,
hrmemployeehdr.DateOfJoin,
hlocationhdr.locationname,
hrmEmployeeIdentityDtl.IDProofReferenceNo AS [National ID],
(CASE
WHEN hrmEmployeeDeductionSettingsDtl.DeductionCode = 1 THEN hrmEmployeeDeductionSettingsDtl.EmployeeRegID
ELSE ''
END) AS NHIF,
(CASE
WHEN hrmEmployeeDeductionSettingsDtl.DeductionCode = 2 THEN hrmEmployeeDeductionSettingsDtl.EmployeeRegID
ELSE ''
END) AS NSSF,
(CASE
WHEN hrmEmployeeDeductionSettingsDtl.DeductionCode = 3 THEN hrmEmployeeDeductionSettingsDtl.EmployeeRegID
ELSE ''
END) AS KRA,
hrmemployeestatusdtl.Email AS [Employee Email],
huser.email AS [User Account Email],
hrmEmployeeGradeHdr.GradeName,
hDepartment.DepartmentName
FROM hrmemployeehdr
JOIN hrmemployeestatusdtl ON hrmemployeestatusdtl.employeeslno = hrmemployeehdr.employeeslno
JOIN hdivision ON hdivision.divisioncode = hrmemployeestatusdtl.divisioncode
JOIN hlocationhdr ON hlocationhdr.locationcode = hrmemployeestatusdtl.workinglocationcode
JOIN hDepartment ON hDepartment.DepartmentCode = hrmemployeestatusdtl.DepartmentCode
JOIN hrmDesignationHdr ON hrmDesignationHdr.DesignationCode = hrmemployeestatusdtl.DesignationCode
JOIN hrmEmployeeCategoryHdr ON hrmEmployeeCategoryHdr.CategoryCode = hrmemployeestatusdtl.CategoryCode
JOIN hrmEmployeeGradeHdr ON hrmEmployeeGradeHdr.GradeCode = hrmemployeestatusdtl.GradeCode
LEFT JOIN huser ON huser.employeeslno = hrmemployeehdr.employeeslno
JOIN hMasterValue ON hMasterValue.MasterValueID = hrmemployeestatusdtl.MasterValue_EmploymentStatusID
JOIN hrmEmployeeIdentityDtl ON hrmEmployeeIdentityDtl.EmployeeSlno = hrmemployeehdr.EmployeeSlno
INNER JOIN hMasterValue a ON a.MasterValueID = hrmEmployeeIdentityDtl.MasterValue_IDProofTypeID
INNER JOIN hrmEmployeeDeductionSettingsDtl ON hrmEmployeeDeductionSettingsDtl.EmployeeSlno = hrmemployeehdr.EmployeeSlno
LEFT JOIN hrmDeductionHdr ON hrmDeductionHdr.DeductionCode = hrmEmployeeDeductionSettingsDtl.DeductionCode
WHERE hrmemployeestatusdtl.employeeslno NOT IN (SELECT hrmemploymentstoppageandtermination.employeeslno
FROM hrmemploymentstoppageandtermination)
AND hrmEmployeeIdentityDtl.MasterValue_IDProofTypeID = 2741005
--and hrmemployeestatusdtl.email = huser.email
--and huser.isemployee = 1
-- select * from huser
ORDER BY employeefirstname ASC;
Use conditional aggregation on the detail table to condense all those rows into one for each employee. Something like:
with edet as (select employeeslno,
max(CASE DeductionCode when 1 THEN EmployeeRegID ELSE '' END) AS NHIF,
max(CASE DeductionCode when 2 THEN EmployeeRegID ELSE '' END) AS NSSF,
max(CASE DeductionCode when 3 THEN EmployeeRegID ELSE '' END) AS KRA
from dbo.hrmEmployeeDeductionSettingsDtl
group by employeeslno)
select emp.employeeslno, ...,
edet.NHIF, edet.NSSF, edet.KRA, ...
from dbo.hrmemployeehdr as emp
inner join edet on emp.employeeslno = edet.employeeslno
...
order by ...
;
Notice the formatting changes that HELP everyone read and understand the code as well as the good habits of using aliases, schema-qualified table names, statement terminator, etc. As already mentioned, the other joins might be contributing to the problem - but this addresses the 1:3 relationship between the header and detail table.

SQL Outer Join views

first of all sorry the question does not describes my problem and I don't know how to tell it as question.
I want to count how many women and men with position supervisor works in every branch.
So this is the SQL:
CREATE VIEW Women (BranchID,AnzahlF,position ) AS
SELECT Branch.BranchID, COUNT(*) As AnzahlF,Staff.position
FROM Staff full outer JOIN
Branch ON Branch.BranchID = Staff.BranchFK
WHERE gender LIKE 'F' AND position LIKE 'Supervisor'
GROUP BY Branch.BranchID,Staff.position
CREATE VIEW Men (BranchID,AnzahlM,position ) AS
SELECT Branch.BranchID,COUNT(*) As AnzahlM ,Staff.position
FROM Staff Full outer JOIN
Branch ON Branch.BranchID = Staff.BranchFK
WHERE gender LIKE 'M' AND position LIKE 'Supervisor'
GROUP BY Branch.BranchID,Staff.position
Select ISNULL(Women.BranchID, Men.BranchID) AS BranchID,
Case When (Women.AnzahlF is Null) THEN 0
ELSE Women.AnzahlF END As ANzahlFSuperv,
Case When (Men.AnzahlM is Null) THEN 0
ELSE Men.AnzahlM END As ANzahlMSuperv
from Women Full outer join Men On Women.BranchID = Men.BranchID
Group by Women.BranchID, Men.BranchID,Women.ANzahlF,Men.AnzahlM Order by BranchID
This is the output:
BranchID,ANzahlFSuperv, ANzahlMSuperv
B001,2,0
B003,1,1
B004,1,1
B005,1,0
B006,1,0
B007,0,2
B008,1,1
B009,0,1
B010,0,1
B011,1,0
B012,0,1
B013,1,0
B014,1,0
=> Missing B002, 0,0
But I do not get the Branch with ID 'B002' it has ANzahlFSuperv = 0 and ANzahlMSuperv = 0. So it has no supersivisor. So how to get this result?
The solution must be in the views so how to get this BranchID.
I tried everything but worthless.
Hope you guys can hep me!
Thanks!
Why would you use views for this? Just a left join and group by:
SELECT b.BranchID, COUNT(*) As AnzahlM,
SUM(CASE WHEN s.gender = 'F' THEN 1 ELSE 0 END) as females,
SUM(CASE WHEN s.gender = 'M' THEN 1 ELSE 0 END) as males
FROM Branch b LEFT JOIN
Staff s
ON b.BranchID = s.BranchFK AND s.position = 'Supervisor'
GROUP BY b.BranchID;

SQL Server / T-SQL : query optimization assistance

I have this QA logic that looks for errors into every AuditID within a RoomID to see if their AuditType were never marked Complete or if they have two complete statuses. Finally, it picks only the maximum AuditDate of the RoomIDs with errors to avoid showing multiple instances of the same RoomID, since there are many audits per room.
The issue is that the AUDIT table is very large and takes a long time to run. I was wondering if there is anyway to reach the same result faster.
Thank you in advance !
IF object_ID('tempdb..#AUDIT') is not null drop table #AUDIT
IF object_ID('tempdb..#ROOMS') is not null drop table #ROOMS
IF object_ID('tempdb..#COMPLETE') is not null drop table #COMPLETE
IF object_ID('tempdb..#FINALE') is not null drop table #FINALE
SELECT distinct
oc.HotelID, o.RoomID
INTO #ROOMS
FROM dbo.[rooms] o
LEFT OUTER JOIN dbo.[hotels] oc on o.HotelID = oc.HotelID
WHERE
o.[status] = '2'
AND o.orderType = '2'
SELECT
t.AuditID, t.RoomID, t.AuditDate, t.AuditType
INTO
#AUDIT
FROM
[dbo].[AUDIT] t
WHERE
t.RoomID IN (SELECT RoomID FROM #ROOMS)
SELECT
t1.RoomID, t3.AuditType, t3.AuditDate, t3.AuditID, t1.CompleteStatus
INTO
#COMPLETE
FROM
(SELECT
RoomID,
SUM(CASE WHEN AuditType = 'Complete' THEN 1 ELSE 0 END) AS CompleteStatus
FROM
#AUDIT
GROUP BY
RoomID) t1
INNER JOIN
#AUDIT t3 ON t1.RoomID = t3.RoomID
WHERE
t1.CompleteStatus = 0
OR t1.CompleteStatus > 1
SELECT
o.HotelID, o.RoomID,
a.AuditID, a.RoomID, a.AuditDate, a.AuditType, a.CompleteStatus,
c.ClientNum
INTO
#FINALE
FROM
#ROOMS O
LEFT OUTER JOIN
#COMPLETE a on o.RoomID = a.RoomID
LEFT OUTER JOIN
[dbo].[clients] c on o.clientNum = c.clientNum
SELECT
t.*,
Complete_Error_Status = CASE WHEN t.CompleteStatus = 0
THEN 'Not Complete'
WHEN t.CompleteStatus > 1
THEN 'Complete More Than Once'
END
FROM
#FINALE t
INNER JOIN
(SELECT
RoomID, MAX(AuditDate) AS MaxDate
FROM
#FINALE
GROUP BY
RoomID) tm ON t.RoomID = tm.RoomID AND t.AuditDate = tm.MaxDate
One section you could improve would be this one. See the inline comments.
SELECT
t1.RoomID, t3.AuditType, t3.AuditDate, t3.AuditID, t1.CompleteStatus
INTO
#COMPLETE
FROM
(SELECT
RoomID,
COUNT(1) AS CompleteStatus
-- Use the above along with the WHERE clause below
-- so that you are aggregating fewer records and
-- avoiding a CASE statement. Remove this next line.
--SUM(CASE WHEN AuditType = 'Complete' THEN 1 ELSE 0 END) AS CompleteStatus
FROM
#AUDIT
WHERE
AuditType = 'Complete'
GROUP BY
RoomID) t1
INNER JOIN
#AUDIT t3 ON t1.RoomID = t3.RoomID
WHERE
t1.CompleteStatus = 0
OR t1.CompleteStatus > 1
Just a thought. Streamline your code and your solution. you are not effectively filtering your datasets smaller so you continue to query the entire tables which is taking a lot of your resources and your temp tables are becoming full copies of those columns without the indexes (PK, FK, ++??) on the original table to take advantage of. This by no means is a perfect solution but it is an idea of how you can consolidate your logic and reduce your overall data set. Give it a try and see if it performs better for you.
Note this will return the last audit record for any room that has either not had an audit completed or completed more than once.
;WITH cte AS (
SELECT
o.RoomId
,o.clientNum
,a.AuditId
,a.AuditDate
,a.AuditType
,NumOfAuditsComplete = SUM(CASE WHEN a.AuditType = 'Complete' THEN 1 ELSE 0 END) OVER (PARTITION BY o.RoomId)
,RowNum = ROW_NUMBER() OVER (PARTITION BY o.RoomId ORDER BY a.AuditDate DESC)
FROm
dbo.Rooms o
LEFT JOIN dbo.Audit a
ON o.RoomId = a.RoomId
WHERE
o.[Status] = 2
AND o.OrderType = 2
)
SELECT
oc.HotelId
,cte.RoomId
,cte.AuditId
,cte.AuditDate
,cte.AuditType
,cte.NumOfAuditsComplete
,cte.clientNum
,Complete_Error_Status = CASE WHEN cte.NumOfAuditsComplete > 1 THEN 'Complete More Than Once' ELSE 'Not Complete' END
FROM
cte
LEFT JOIN dbo.Hotels oc
ON cte.HotelId = oc.HotelId
LEFT JOIN dbo.clients c
ON cte.clientNum = c.clientNum
WHERE
cte.RowNum = 1
AND cte.NumOfAuditsComplete != 1
Also note I changed your
WHERE
o.[status] = '2'
AND o.orderType = '2'
TO
WHERE
o.[status] = 2
AND o.orderType = 2
to be numeric without the single quotes. If the data type is truely varchar add them back but when you query a numeric column as a varchar it will do data conversion and may not take advantage of indexes that you have built on the table.

how to include several count functions and group byes in one line

I am trying to get the number of customers by their types and groups all in line as such:
GroupName | GroupNotes | Count(Type1) | Count(Type2) | Count(Type3)
but instead I can only get the groupid ,the typeid and the number of types in the group by using the following query
SELECT
CustomersGroups.idCustomerGroup , Customers.type , COUNT(*)
FROM
CustomersGroups
inner Join CustomersInGroup on CustomersGroups.idCustomerGroup = CustomersInGroup.idCustomerGroup
inner Join Customers on Customers.idCustomer = CustomersInGroup.idCustomer
Group by
CustomersGroups.idCustomerGroup, Customers.type
is there a way to show them in a single line , (and show the name of the group?)
This is a "pivot" query. Some databases directly support pivot syntax. In all, you can use conditional aggregation.
Perhaps more importantly, you should learn to use table aliases. These make queries easier to write and to read:
select cg.idCustomerGroup,
sum(case when c.type = 'Type1' then 1 else 0 end) as num_type1,
sum(case when c.type = 'Type2' then 1 else 0 end) as num_type2,
sum(case when c.type = 'Type3' then 1 else 0 end) as num_type3
from CustomersGroups cg inner Join
CustomersInGroup cig
on cg.idCustomerGroup = cig.idCustomerGroup inner Join
Customers c
on c.idCustomer = cig.idCustomer
Group by cg.idCustomerGroup;

SQL joins returning multiple results

select
tmp.templatedesc Template
,sec.name Section
,q.questiontext Questions,
--,sum(case when q.responserequired = '0' then 1 else null end) as 'N/A'
--,sum(case when q.responserequired = '1' then 1 else null end) as Scored
--,count (case when (qr.weightedscore is not null and tmp.templatedesc = 'QA 30 Day Call Form' and
--sec.name = 'opening' and
--rv.reviewstatusid = 1 )then 1 else null end) as scored
----,(case when qr.weightedscore <> q.weight then rv.reviewid else null end) as fail
--count (case when qr.weightedscore is null then 1 else null end) NA,
--count (case when qr.weightedscore is not null then 1 else null end) scored,
sec.sequencenumber, q.questionnumber, qr.*
from
aqm.dbo.reviewtemplate tmp (nolock)
inner join aqm.dbo.section sec on sec.templateid =tmp.templateid
inner join aqm.dbo.sectionresult scr on scr.sectionid = sec.sectionid
inner join aqm.dbo.questionresult qr on qr.sectionresultid = scr.sectionresultid
inner join aqm.dbo.question q on q.questionid = qr.questionid
--inner join aqm.dbo.questiontype qt on qt.questiontypeid = q.questiontypeid
--left outer join aqm.dbo.questionoption qo on qo.questionid = q.questionid
inner join aqm.dbo.review rv on tmp.templateid = rv.templateid
inner join aqm.dbo.media md on md.mediaid = rv.mediaid
inner join aqm.dbo.iqmuser ut on md.userid = ut.userid
where
rv.reviewstatusid = 1 and
tmp.templatedesc = 'QA 30 Day Call Form'
and sec.name = 'opening' and
convert(varchar,dateadd(hh,-7,rv.reviewdate), 101) = '07/07/2014'
and ut.windowslogonaccount = 'name.name'
and q.questionnumber = 4
--group by
--tmp.templatedesc , sec.name, q.questiontext, sec.sequencenumber, q.questionnumber
order by
sec.sequencenumber, q.questionnumber
the questionresultid and sectionresultid are returning multiple values
how can i fix the joins so that it doesnt return multiple values?
i have it drilled down to a date and a person so that it should only return one row of results( but that obviously didnt work)
not sure what other data i can provide
update
i think it has to do with joins
inner join aqm.dbo.sectionresult scr on scr.sectionid = sec.sectionid
inner join aqm.dbo.questionresult qr on qr.sectionresultid = scr.sectionresultid
as those are the ones returning multiple results.
just dont know how to fix it
First, neither aqm.dbo.questiontype nor aqm.dbo.questionoption are used in your return fields or your where clause so get rid of them if they aren't required.
Second, you are OUTER JOINing on the aqm.dbo.review, but the reviewstatusid and reviewdate are required in the WHERE clause - so this should probably be an INNER JOIN.
Last, best way to debug issues like this is to comment out the COUNT statements and the GROUP BY clause - and see what raw data is being returned.