Count Frequency based on Bit Flag - sql

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

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 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.

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

TSQL - TOP and COUNT in one SELECT

i try to combine these two statements to one, but all my tries failed!
Is it possible to merge them?
-- Is there a open answer?
SELECT CASE COUNT(tbl_Communication.pk_Communication) WHEN 0
THEN 0 ELSE 1 END AS hasAnsweredCom
FROM tbl_Communication
JOIN tbl_CommunicationElements ON tbl_CommunicationElements.pk_Communication = tbl_Communication.pk_Communication
WHERE tbl_Communication.pk_Ticket = #pk_Ticket
AND tbl_Communication.isClosed = 0
AND tbl_Communication.pk_CommunicationType = (SELECT pk_CommunicationType
FROM tbl_CommunicationType
WHERE name = 'query')
-- Get the answer text
SELECT TOP 1 tbl_Communication.subject AS hasAnsweredComStepName
FROM tbl_Communication
JOIN tbl_CommunicationElements ON tbl_CommunicationElements.pk_Communication = tbl_Communication.pk_Communication
WHERE tbl_Communication.pk_Ticket = #pk_Ticket
AND tbl_Communication.isClosed = 0
AND tbl_Communication.pk_CommunicationType = (SELECT pk_CommunicationType
FROM tbl_CommunicationType
WHERE name = 'query')
ORDER BY tbl_Communication.pk_Communication
Right join trick.
SELECT TOP 1
CASE WHEN tbl_CommunicationElements.pk_Communication IS NULL THEN 0 ELSE 1 END hasAnsweredCom
, tbl_Communication.subject AS hasAnsweredComStepName
FROM tbl_Communication
JOIN tbl_CommunicationElements ON tbl_CommunicationElements.pk_Communication = tbl_Communication.pk_Communication
RIGHT JOIN (VALUES(1)) AS Ext(x) ON (
tbl_Communication.pk_Ticket = #pk_Ticket
AND tbl_Communication.isClosed = 0
AND tbl_Communication.pk_CommunicationType = (SELECT pk_CommunicationType
FROM tbl_CommunicationType
WHERE name = 'query')
)
If you are willing to put the two results on one line, the following works:
select (CASE count(*) WHEN 0 THEN 0 ELSE 1 END) AS hasAnsweredCom,
MAX(case when seqnum = 1 then subject end) as hasAnsweredComStepName
from (SELECT tbl_Communication.pk_Communication, tbl_Communication.subject,
ROW_NUMBER() over (order by pk_communication) as seqnum
FROM tbl_Communication
JOIN tbl_CommunicationElements ON tbl_CommunicationElements.pk_Communication = tbl_Communication.pk_Communication
WHERE tbl_Communication.pk_Ticket = #pk_Ticket
AND tbl_Communication.isClosed = 0
AND tbl_Communication.pk_CommunicationType = (SELECT pk_CommunicationType
FROM tbl_CommunicationType
WHERE name = 'query')
) t
The second value will be NULL if there are no answers.
As for returning two rows. My guess is that subject is a string whereas hasAnsweredCom is an integer. The types conflict, so any sort of union or bringing the results together will probably result in a type conflict on the second row.

Having an issue with t-sql and case statement with and/where clause

SELECT 3 AS LoggedIn, 83 AS MessageID, Data AS DistID, 1 AS StatusID
, 0 AS Important
, CASE WHEN (dd.SponsorID = 3) THEN 0 ELSE d.ReceiveUplineMessages END AS Test
, dd.SponsorID
FROM msg_SplitVar(',', #MessageList) sv
INNER JOIN Distributor d
ON d.DistID = sv.Data
INNER JOIN DistributorDetail dd
ON dd.DistID = sv.Data
WHERE Data NOT IN (SELECT DistID FROM MessageBlockList WHERE BlockedID = 3)
AND Test = 0
I have a field set in the distributor table ReceiveUplineMessages. This can be set to 0 or 1.
If they set this to 0 I only want to send them a message if it is from their sponsor (logged in)
I have the case statement working, but I'm having trouble with the AND clause.
The splitVar function just takes a string based array '3,4,5,6,7' and splits the data out to individual rows for an insert statement.
Anyone know what I am doing wrong in the AND clause? I only want them to show up if ReceiveUplineMessages is a 0.
try not using your Test alias in your WHERE clause:
SELECT 3 AS LoggedIn, 83 AS MessageID, Data AS DistID, 1 AS StatusID
, 0 AS Important
, CASE WHEN (dd.SponsorID = 3) THEN 0 ELSE d.ReceiveUplineMessages END AS Test
, dd.SponsorID
FROM msg_SplitVar(',', #MessageList) sv
INNER JOIN Distributor d
ON d.DistID = sv.Data
INNER JOIN DistributorDetail dd
ON dd.DistID = sv.Data
WHERE Data NOT IN (SELECT DistID FROM MessageBlockList WHERE BlockedID = 3)
AND (CASE WHEN (dd.SponsorID = 3) THEN 0 ELSE d.ReceiveUplineMessages END) = 0
or wrap your statement in another SELECT to use your Test in the WHERE:
SELECT t.*
FROM
(
SELECT 3 AS LoggedIn, 83 AS MessageID, Data AS DistID, 1 AS StatusID
, 0 AS Important
, CASE WHEN (dd.SponsorID = 3) THEN 0 ELSE d.ReceiveUplineMessages END AS Test
, dd.SponsorID
FROM msg_SplitVar(',', #MessageList) sv
INNER JOIN Distributor d
ON d.DistID = sv.Data
INNER JOIN DistributorDetail dd
ON dd.DistID = sv.Data
WHERE Data NOT IN (SELECT DistID FROM MessageBlockList WHERE BlockedID = 3)
) t
WHERE t.Test = 0