Convert multiple rows to columns but as one rows - sql

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.

Related

How do I filter out a group where there is at least one occurrence of a value?

In sample data below I need to show only records where the ContactType is not equal to 'CLOSR' by caseid_i but with my code below it filters out anything with CLOSR, but not by the CaseID_I. So if at least one of the Caseid_i has a ContactType of 'CLOSR' I need to filter out the whole CaseID. So for CaseID_i 51709, that caseid_i wouldn't show up in my results at all because at least one of the 'ContactTypes' is CLOSR and the same for 51715.
SELECT C.[cdcpincid_c]
,I.[caseid_i]
,I.echono_c
,C.[close_d] AS ClosureDate
,C.[clinician_c] AS ClosureClinician
,N.[contacttyp_c] AS ContactType
,SC.StaffName AS ClosureStaffName
,I.Refdate_d
FROM [cd].[tb_cdcp_case] C
INNER JOIN [cd].[tb_cdcp_incident] I ON I.[uniqueid_c] = C.[cdcpincid_c]
INNER JOIN [cd].[tb_cdcp_clientcontact] N ON I.[uniqueid_c] = N.[cdcpincid_c]
LEFT OUTER JOIN [dbo].[vCDCP_Staff] SC ON C.clinician_c = SC.Staffcode_c
GROUP BY C.[cdcpincid_c]
,I.[caseid_i]
,I.echono_c
,C.[close_d]
,C.[clinician_c]
,N.[contacttyp_c]
,SC.StaffName
,I.Refdate_d
HAVING sum(CASE
WHEN N.[contacttyp_c] = 'CLOSR'
THEN 1
ELSE 0
END) = 0
AND C.[close_d] IS NOT NULL
AND I.echono_c <> 'OC'
Remove the condition from the where clause and switch it to a having clause:
having sum(case when ContactType = 'CLOSR' then 1 else 0 end) = 0
EDIT:
I see. You are returning multiple rows per caseid. In that case, use a window function as well:
having sum(sum(case when ContactType = 'CLOSR' then 1 else 0 end)) over (partition by caseid) = 0

Return a Count of 0 When No Rows

OK, I've looked this up and tried a number of solutions, but can't get it to work. I'm a bit of a novice. Here's my original query - how can I get it to return 0 for an account when there are no results in the student table?
SELECT a.NAME
,count(s.student_sid)
FROM account a
JOIN inst i ON a.inst_sid = i.root_inst_sid
JOIN inst_year iy ON i.inst_sid = iy.inst_sid
JOIN student s ON iy.inst_year_sid = s.inst_year_sid
WHERE s.demo = 0
AND s.STATE = 1
AND i.STATE = 1
AND iy.year_sid = 16
AND a.account_sid IN (
20187987
,20188576
,20188755
,52317128
,20189249
)
GROUP BY a.NAME;
Use an outer join, moving the condition on that table into the join:
select a.name, count(s.student_sid)
from account a
join inst i on a.inst_sid = i.root_inst_sid
join inst_year iy on i.inst_sid = iy.inst_sid
left join student s on iy.inst_year_sid = s.inst_year_sid
and s.demo = 0
and s.state = 1
where i.state = 1
and iy.year_sid = 16
and a.account_sid in (20187987, 20188576, 20188755, 52317128, 20189249)
group by a.name;
count() does not count null values, which s.student_sid will be if no rows join from student.
You need to LEFT JOIN and then SUM() over the group where s.student_sid is not null:
select
a.name,
sum(case when s.student_sid is null then 0 else 1 end) as student_count
from account a
join inst i on a.inst_sid = i.root_inst_sid
join inst_year iy on i.inst_sid = iy.inst_sid
left join student s
on iy.inst_year_sid = s.inst_year_sid
and s.demo = 0
and s.state = 1
where i.state = 1
and iy.year_sid = 16
and a.account_sid in (20187987, 20188576, 20188755, 52317128, 20189249)
group by a.name;
This is assuming that all of the fields in the student table that you are filtering on are optional. If you don't want to enforce removal of records where, say, s.state does not equal 1, then you need to move the s.state=1 predicate into the WHERE clauses.
If, for some reason, you are getting duplicate student IDs and students are being counted twice, then you can change the aggregate function to this:
count(distinct s.student_id) as student_count
...which is safe to do as count(distinct ...) ignores null values.

Count Frequency based on Bit Flag

SELECT
ROW_NUMBER() OVER (PARTITION BY dicei.IsLocked ORDER BY DocumentInstanceChapterExpanded.PK_DocumentInstanceChapterExpanded)
,DocumentInstance.PK_DocumentInstance
,DocumentInstanceChapterExpanded.PK_DocumentInstanceChapterExpanded
,dicei.IsLocked
FROM DocumentInstance INNER JOIN
DocumentInstanceChapter ON DocumentInstance.PK_DocumentInstance = DocumentInstanceChapter.FK_DocumentInstance INNER JOIN
DocumentInstanceChapter AS DocumentInstanceChapter_1 ON
DocumentInstanceChapter.PK_DocumentInstanceChapter = DocumentInstanceChapter_1.FK_DocumentInstanceChapter INNER JOIN
DocumentInstanceChapterExpanded ON
DocumentInstanceChapter_1.PK_DocumentInstanceChapter = DocumentInstanceChapterExpanded.FK_DocumentInstanceChapter INNER JOIN
DocumentInstanceChapterExpanded AS DocumentInstanceChapterExpanded_1 ON
DocumentInstanceChapter.PK_DocumentInstanceChapter = DocumentInstanceChapterExpanded_1.FK_DocumentInstanceChapter INNER JOIN
DocumentInstanceChapterExpandedItem AS dicei ON
DocumentInstanceChapterExpanded.PK_DocumentInstanceChapterExpanded = dicei.FK_DocumentInstanceChapterExpanded
WHERE (DocumentInstance.PK_DocumentInstance = 455)
AND DocumentInstanceChapterExpanded_1.PK_DocumentInstanceChapterExpanded = 50730
As you can see the picture what i wanted to do was add a Column which would indicate
**Result Expected**
ExpandeditemKey IsLocked StatusColumn
50797 0 Mixed
50797 0 Mixed
50797 1 Mixed
50797 1 Mixed
50797 1 Mixed
50798 1 Lock
50798 1 Lock
50798 1 Lock
If it contains 0 and 1 'Mixed'
If it contains 1 only 'Lock'
If it contains 0 only 'Unlock'
it does not necessary need to be string column, i tried using OverBy Clause if i can used Partition by for the Islock Bit Field but was not able to
Thanks for having a look.
I suggest usng MIN/MAX as window functions, then you can add a case expression that works across a partition. e.g.
SELECT
ROW_NUMBER() OVER (PARTITION BY dicei.IsLocked ORDER BY DocumentInstanceChapterExpanded.PK_DocumentInstanceChapterExpanded)
, DocumentInstance.PK_DocumentInstance
, DocumentInstanceChapterExpanded.PK_DocumentInstanceChapterExpanded
, dicei.IsLocked
, case when dicei.isLockedMin <> dicei.isLockedMax then 'Mixed'
when dicei.isLockedMax = 0 then 'Unlocked'
else 'Locked'
end StatusColumn
FROM DocumentInstance
INNER JOIN DocumentInstanceChapter ON DocumentInstance.PK_DocumentInstance = DocumentInstanceChapter.FK_DocumentInstance
INNER JOIN DocumentInstanceChapter AS documentinstancechapter_1 ON DocumentInstanceChapter.PK_DocumentInstanceChapter = documentinstancechapter_1.FK_DocumentInstanceChapter
INNER JOIN DocumentInstanceChapterExpanded ON documentinstancechapter_1.PK_DocumentInstanceChapter = DocumentInstanceChapterExpanded.FK_DocumentInstanceChapter
INNER JOIN DocumentInstanceChapterExpanded AS documentinstancechapterexpanded_1 ON DocumentInstanceChapter.PK_DocumentInstanceChapter = documentinstancechapterexpanded_1.FK_DocumentInstanceChapter
INNER JOIN (
select d.*
, min(d.IsLocked) over(partition by d.FK_DocumentInstanceChapterExpanded) isLockedMin
, max(d.IsLocked) over(partition by d.FK_DocumentInstanceChapterExpanded) isLockedMax
from DocumentInstanceChapterExpandedItem d
) AS dicei ON DocumentInstanceChapterExpanded.PK_DocumentInstanceChapterExpanded = dicei.FK_DocumentInstanceChapterExpanded
WHERE (DocumentInstance.PK_DocumentInstance = 455)
AND documentinstancechapterexpanded_1.PK_DocumentInstanceChapterExpanded = 50730
edit, due to the bit column, case expressions were needed:
, min(case when d.IsLocked = 1 then 1 else 0 end) over(partition by d.FK_DocumentInstanceChapterExpanded) isLockedMin
, max(case when d.IsLocked = 1 then 1 else 0 end) over(partition by d.FK_DocumentInstanceChapterExpanded) isLockedMax

SQL Condition for sum

I have a sql statement with many inner join tables, as you can see below I have many conditional SUM statements , these sums are giving me wrong (very large) numbers as the inner join is repeating the same values in my source select pool. I was wondering id there is a way to limit these sum conditions lets say to EMPLIDs. The code is :
SELECT
A.EMPL_CTG,
B.DESCR AS PrName,
SUM(A.CURRENT_COMPRATE) AS SALARY_COST_BUDGET,
SUM(A.BUDGET_AMT) AS BUDGET_AMT,
SUM(A.BUDGET_AMT)*100/SUM(A.CURRENT_COMPRATE) AS MERIT_GOAL,
SUM(C.FACTOR_XSALARY) AS X_Programp,
SUM(A.FACTOR_XSALARY) AS X_Program,
COUNT(A.EMPLID) AS EMPL_CNT,
COUNT(D.EMPLID),
SUM(CASE WHEN A.PROMOTION_SECTION = 'Y' THEN 1 ELSE 0 END) AS PRMCNT,
SUM(CASE WHEN A.EXCEPT_IND = 'Y' THEN 1 ELSE 0 END) AS EXPCNT,
(SUM(CASE WHEN A.PROMOTION_SECTION = 'Y' THEN 1 ELSE 0 END)+SUM(CASE WHEN A.EXCEPT_IND = 'Y' THEN 1 ELSE 0 END))*100/(COUNT(A.EMPLID)) AS PEpercent
FROM
EMP_DTL A INNER JOIN EMPL_CTG_L1 B ON A.EMPL_CTG = B.EMPL_CTG
INNER JOIN
ECM_PRYR_VW C ON A.EMPLID=C.EMPLID
INNER JOIN ECM_INELIG D on D.EMPL_CTG=A.EMPL_CTG and D.YEAR=YEAR(getdate())
WHERE
A.YEAR=YEAR(getdate())
AND B.EFF_STATUS='A'
GROUP BY
A.EMPL_CTG,
B.DESCR
ORDER BY B.DESCR
I already tried moving D.YEAR=YEAR(getdate()) to the where clause. Any help would be greatly appereciated
The probable reason of your very large numbers is probably due to the result of Cartesian product of joining A -> B, A -> C and A -> D where tables C and D appear to have multiple records. So, just example... if A has 10 records, and C has 10 for each of the A records, you now have 10 * 10 records... Finally, join that to D table with 10 records, you now have 10 * 10 * 10 for each "A", thus your bloated answers.
Now, how to resolve. I have taken your "C" and "D" tables and "Pre-Aggregated" those counts based on the join column basis. This way, they will each have only 1 record with the total already computed at that level, joined back to A table and you lose your Cartesian issue.
Now, for table B, it appears that is a lookup table only and would only be a single record result anyhow.
SELECT
A.EMPL_CTG,
B.DESCR AS PrName,
SUM(A.CURRENT_COMPRATE) AS SALARY_COST_BUDGET,
SUM(A.BUDGET_AMT) AS BUDGET_AMT,
SUM(A.BUDGET_AMT)*100/SUM(A.CURRENT_COMPRATE) AS MERIT_GOAL,
PreAggC.X_Programp,
SUM(A.FACTOR_XSALARY) AS X_Program,
COUNT(A.EMPLID) AS EMPL_CNT,
PreAggD.DCount,
SUM(CASE WHEN A.PROMOTION_SECTION = 'Y' THEN 1 ELSE 0 END) AS PRMCNT,
SUM(CASE WHEN A.EXCEPT_IND = 'Y' THEN 1 ELSE 0 END) AS EXPCNT,
( SUM( CASE WHEN A.PROMOTION_SECTION = 'Y' THEN 1 ELSE 0 END
+ CASE WHEN A.EXCEPT_IND = 'Y' THEN 1 ELSE 0 END ) *
100 / COUNT(A.EMPLID) AS PEpercent
FROM
EMP_DTL A
INNER JOIN EMPL_CTG_L1 B
ON A.EMPL_CTG = B.EMPL_CTG
AND B.EFF_STATUS='A'
INNER JOIN ( select
C.EMPLID,
SUM(C.FACTOR_XSALARY) AS X_Programp
from
ECM_PRYR_VW C
group by
C.EMPLID ) PreAggC
ON A.EMPLID = PreAggC.EMPLID
INNER JOIN ( select
D.EMPLID,
COUNT(*) AS DCount
from
ECM_INELIG D
where
D.Year = YEAR( getdate())
group by
D.EMPLID ) PreAggD
ON A.EMPLID = PreAggD.EMPLID
WHERE
A.YEAR=YEAR(getdate())
GROUP BY
A.EMPL_CTG,
B.DESCR
ORDER BY
B.DESCR

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.