Find out Monthly Percentage in SQL - sql-server

I am trying to find out monthly percentage (last 12 Months) for the below query
with severity wise, it would be great if anyone could help me out
SELECT Month(a.[Work Initated Date]) AS Month, a.[Initial Severity],
(CAST(Count(a.[Case Number]) AS Decimal(10,1))/ (select CAST(Count(a.[Case Number]) AS Decimal(10,1))
FROM [DSTRINING].[dbo].[SFDC WorkInitiated] (nolock) a
where a.[Initial Severity]='Critical' AND Month(a.[Work Initated Date])=Month(getdate()))*100) AS 'Work Initated%'
FROM [DSTRINING].[dbo].[SFDC WorkInitiated] (nolock) a
where a.[Initial Severity]='Critical' AND Month(a.[Work Initated Date])=Month(getdate()) AND a.[Work Initiated Target Breached]='NO'
Group by Month(a.[Work Initated Date]), a.[Initial Severity]

You can use window functions for this. Your query is a bit hard to follow, but . . .
select Year(wi.[Work Initated Date]) AS yyyy, Month(wi.[Work Initated Date]) AS mm,
wi.[Initial Severity],
count(*) * 100.0/ sum(count(*)) over partition by Year(wi.[Work Initated Date]), Month(wi.[Work Initated Date])) as [Work Initated%]
from [DSTRINING].[dbo].[SFDC WorkInitiated] wi
where wi.[Initial Severity] = 'Critical' and
Month(wi.[Work Initated Date]) = Month(getdate())) and
wi.[Work Initiated Target Breached] = 'NO'
Group by Year(wi.[Work Initated Date]), Month(wi.[Work Initated Date]), wi.[Initial Severity];
Notes:
I included the year along with the month. I just think that is a good practice to avoid unwanted errors.
I changed the table alias to the initials of the table. That makes the query easier to follow.
The window function in the select calculates the ratio you want.
I removed the conversion to decimal(10, 1). I think that confuses the query (although you can add it back in for your output purposes).

Related

T-SQL split result into two rows

I'm building a customer statement report and have the below query.
SELECT TOP (100) PERCENT dbo.vCustomerTransactions.datTrans AS InvDate
,dbo.vCustomerTransactions.strDocRef
,SUM(dbo.vCustomerTransactions.intAmountTax) AS Debit
,ISNULL(dbo.Cashbook.strTransRef, 'None') AS strTransRef
,ISNULL(SUM(dbo.Cashbook.intAmount), 0) AS Credit
,ISNULL(dbo.Cashbook.datCaptured, 0) AS PaymentDate
FROM dbo.vCustomerTransactions
LEFT OUTER JOIN dbo.Cashbook ON dbo.vCustomerTransactions.strDocID = dbo.Cashbook.strDocID
WHERE dbo.vCustomerTransactions.datTrans BETWEEN '2020-02-01 00:00:00'
AND '2020-03-18 00:00:00'
AND dbo.vCustomerTransactions.strTransType IN ('INV')
GROUP BY dbo.vCustomerTransactions.strDocRef
,dbo.Cashbook.strTransRef
,dbo.vCustomerTransactions.datTrans
,dbo.Cashbook.datCaptured
ORDER BY dbo.Cashbook.strTransRef DESC
,dbo.vCustomerTransactions.datTrans
The data is returned as follows.
"2020-02-29 00:00:00" "INV0034004" "1236.7675" "RCPT0007790" "1236.77" "2020-03-10 00:00:00"
What i need to do for that report is split that 1 row into two result rows, as follows
"2020-02-29 00:00:00" "INV0034004" "1236.7675" "0.00"
"2020-03-10 00:00:00" "RCPT0007790" "0.00" "1236.77"
So its splitting the data into debts and credits. I have no idea how to achieve this, please could someone point me in the right direction.
Many Thanks.
I'm making some guesses here, especially in regards that if Credit or Debit has a value of 0 you don't want the row, however using VALUES would likely be better here. It would certainly avoid the overhead of 2 scans of the tables, and the costly UNION (rather than UNION ALL):
WITH CTE AS(
--3 part naming for columns is being removed from SQL Server,. Don't use it.
SELECT --Removed Top, as pointless
CT.datTrans AS InvDate,
CT.strDocRef,
SUM(CT.intAmountTax) AS Debit,
ISNULL(Cb.strTransRef, 'None') AS strTransRef,
ISNULL(SUM(Cb.intAmount), 0) AS Credit,
ISNULL(Cb.datCaptured, 0) AS PaymentDate
FROM dbo.vCustomerTransactions CT
LEFT OUTER JOIN dbo.Cashbook Cb ON CT.strDocID = Cb.strDocID
WHERE CT.datTrans BETWEEN '2020-02-01T00:00:00' AND '2020-03-18T00:00:00'
AND CT.strTransType IN ('INV')
GROUP BY CT.strDocRef,
Cb.strTransRef,
CT.datTrans,
Cb.datCaptured)
SELECT C.InvDate,
C.strDocRef,
V.Debit,
C.strTransRef,
V.Credit,
C.PaymentDate
FROM CTE C
CROSS APPLY (VALUES (0, C.Debit),
(C.Credit, 0)) V (Credit, Debit)
WHERE NOT(V.Debit = 0 AND V.Credit = 0)
ORDER BY C.strTransRef DESC,
C.InvDate;
My approach would be to take the original query and make it into a Common Table Expression (or a temporary table if you want to do it the old school way). And then do two queries - one for credits and one for debits - off of that resultset and UNION them together. Something like this:
WITH cteMain AS (
SELECT TOP (100) PERCENT dbo.vCustomerTransactions.datTrans AS InvDate
,dbo.vCustomerTransactions.strDocRef
,SUM(dbo.vCustomerTransactions.intAmountTax) AS Debit
,ISNULL(dbo.Cashbook.strTransRef, 'None') AS strTransRef
,ISNULL(SUM(dbo.Cashbook.intAmount), 0) AS Credit
,ISNULL(dbo.Cashbook.datCaptured, 0) AS PaymentDate
FROM dbo.vCustomerTransactions
LEFT OUTER JOIN dbo.Cashbook ON dbo.vCustomerTransactions.strDocID = dbo.Cashbook.strDocID
WHERE dbo.vCustomerTransactions.datTrans BETWEEN '2020-02-01 00:00:00'
AND '2020-03-18 00:00:00'
AND dbo.vCustomerTransactions.strTransType IN ('INV')
GROUP BY dbo.vCustomerTransactions.strDocRef
,dbo.Cashbook.strTransRef
,dbo.vCustomerTransactions.datTrans
,dbo.Cashbook.datCaptured
)
SELECT InvDate AS TheDate, strDocRef AS TheRef, Debit, 0.0 AS Credit
FROM cteMain
UNION
SELECT PaymentDate AS TheDate, strTransRef AS TheRef, 0.00 AS Debit, Credit
FROM cteMain
You might also need a WHERE Debit > 0.00 and WHERE Credit > 0.0 in those two SELECT statements, to only pick up the credits/debits that have values, depending on your needs.

Calculate the datediff() between the date 1 and 2

I need to calculate the datediff from one column where the in time marked with a 1 and the out time marked with a 2. if an employee swiped in and there is no out or out but there was no in i would like it to show as null.
I am not sure how I go about doing this.
SELECT
u.userid
,et.name
,CASE
WHEN scs.FullName is NULL THEN u.name
WHEN scs.FullName is NOT NULL THEN scs.FullName
END AS name
,e.LoggedTime AS SwipeTime
,CASE
WHEN et.name = 'Output On By Door' OR et.name = 'User Granted
Entry To Door Using Reading Device' THEN 1
ELSE 2
END AS SwipeTimeDiff
,d.name AS Door
FROM [Users] AS u
LEFT JOIN [Events] AS e ON e.RecordIndex1=u.UserID
LEFT JOIN [EventTypes] AS et on e.EventTypeID = et.EventTypeID
join .[Doors] AS d ON e.RecordIndex2 = d.DoorID
LEFT join SecurityContractorSignIn as scs on scs.Badge = u.lastname
WHERE LoggedTime > CONVERT(DATE, GETDATE()) and d.doorid in (32, 50, 42, 51, 33)
ORDER BY u.name,e.LoggedTime DESC
I would like to have a computed column with the time difference in days, hours and minutes or null if if there is a missing in(1) or out(2) time.
Well, the DATEDIFF() function is fully explained here and the difference for the specific datepart you want to extract is returned as an integer.
According to your need you may do something like but you will have the information in three (or more - if you want to extend) different columns:
-- Calculate the difference of how many days have passed
SELECT DATEDIFF(DAY, LoginTime, LogoutTime) AS DaysPassed
-- Calculate the difference of how many hours have passed
SELECT DATEDIFF(HOUR, LoginTime, LogoutTime) AS HoursPassed
-- Calculate the difference of how minutes have passed
SELECT DATEDIFF(MINUTE, LoginTime, LogoutTime) AS MinutesPassed
If you want to return a string whether the employee logged out or not you may use something like:
SELECT ISNULL(CONVERT(nvarchar(50), DATEDIFF(MONTH, '2019-01-04', NULL)), 'No logout')

datediff inside the time schedule

I need to calculate datedif(ss, start_time, end_time) between certain types of activities, which I pretty know how to do it. My code is:
WITH WorkDates AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY A.NUMBER ORDER BY datestamp DESC) AS RowNumber,
p2.affected_item as Service,
p1.close_time as close_time,
datestamp,
description,
a.type,
a.number as number
from ACTIVITYM1 a
left outer join probsummarym1 p1 on a.number = p1.number
inner join probsummarym2 p2 on p1.number = p2.number
where ((a.type like 'status%'
and (description like '%to Work In Progress%'
or description like '%from Work In Progress%')) or a.type like 'Closed')
)
SELECT
O1.Service,
O1.number,
O1.close_time,
sum(DATEDIFF(ss, O2.datestamp, O1.datestamp)) AS sum_of_time
FROM WorkDates O1
LEFT JOIN WorkDates O2
ON O2.RowNumber = O1.RowNumber + 1
and O2.number = O1.number
where O2.description like '%to Work In Progress%'
and (O1.description like '%from Work In Progress%' or O1.type like 'Closed')
group by O1.Service, O1.number, O1.close_time
order by O1.Service, O1.number
I'm doing it that way, because I need to calculate it between certain types of activities, and a number of such 'time windows' where it should be counted is not constant. And it works quite ok, but it calculates full time, including holidays, weekends
But there is one one more thing I need to have done here.
I would like to calculate datediff(ss, start_date, end_date) but only from Monday to Friday. It means, for example if start_date was on Friday at 16:00 and end_date on Monday at 7:00, datediff should be (24:00 - 16:00) + (07:00 - 00:00) = 15 hours. Is it possible?
You should calculate week difference, then convert it to seconds, then deduct it from datediff.
Like this:
sum(DATEDIFF(ss, O2.datestamp, O1.datestamp) - abs(DATEPART(ww,O2.datestamp) - DATEPART(ww,O1.datestamp)) * 2 * 24 * 60 * 60 ) AS sum_of_time

how to use convert and datepart at the same time

I have here a select command which is used in my crystal report:
SELECT cfvgl.v_id, operator.o_id, cfvgl.cfvgl_no, operator.owner_name,
operator.business_address, vessel_details.ship_name, payment.or_number ,
payment .date_paid , payment.amount , signature.data,
cfvgl.cfvgl_date_released, DATEPART(day, cfvgl.cfvgl_validity_start) as day_issued,
DATEPART(month, cfvgl.cfvgl_validity_start) as month_issued,
DATEPART(year, cfvgl.cfvgl_validity_start) as year,
gear.bag_bunt_mesh_size, gear.body_mesh_size, gear.wing_mesh_size,
cfvgl.catcher_type, cfvgl.other_gear, gear.stretched_depth,
gear.finished_depth, gear.mesh_size, gear.finished_length,
gear.finished_width, gear.total_length, gear.cod_end_mesh_size,
gear.otter_board_size, gear.towing_rope_length, gear.tomweight,
gear.bouyline_length, gear.branchline_length, gear.branchline_distance,
gear.hook_number, gear.hook_type, gear.hook_size, gear.bait_type,
gear.hook_total_number, gear.target_species, gear.mainline_length,
gear.swivel_size, gear.wire_leader_length, gear.netting_type
FROM cfvgl INNER JOIN
vessel_details ON vessel_details.v_id = cfvgl.v_id INNER JOIN
operator ON vessel_details.o_id = operator.o_id LEFT OUTER JOIN
applications ON vessel_details.v_id = applications.v_id LEFT OUTER JOIN
payment ON payment.app_no = applications.app_no LEFT OUTER JOIN
gear ON gear.v_id = vessel_details.v_id LEFT OUTER JOIN
signature ON signature.signature_id = cfvgl.signature_id
my problem is , i have to convert and display the return date of the datepart(month) as words, for example 1 = january ..
i only datepart the month ,
DATEPART(month, cfvgl.cfvgl_validity_start) as month_issued
how will i able to change the format of the datepart month to words?
thanks.
Use DATENAME to get the name.
DATENAME(month, cfvgl.cfvgl_validity_start) as month_issued
Use the DATENAME function to get the month in words like so:
SELECT DATENAME(month, cfvgl.cfvgl_validity_start) AS month_issued
FROM yourtable

in grouping getting duplicate records

I want to filter records on the basis of month. Requirement is in month how much projects are in "completed, pending, started etc" status. This is my query but i am getting duplicate records.
SELECT distinct
convert(varchar(7), w.ExpectedStartDate, 126) AS StatusOfMonth,
COUNT(w.StatusTypeId) AS StatusCount,
w.StatusTypeId,
st.StatusTypeName
FROM Table1 w
LEFT OUTER JOIN StatusType st ON st.StatusTypeId = w.StatusTypeId
WHERE CONVERT(VARCHAR(20), w.ExpectedStartDate, 103) BETWEEN '10/01/2011' AND '14/04/2011'
GROUP BY ExpectedStartDate, w.StatusTypeId, st.StatusTypeName
Please see image to clarify what i want. Please let me know how can i get correct results.
Looks like your grouping by the date, not by the month or by the status of month
Group by
DATEPART(M, ExpectedStartDate)
or
convert(varchar(7), w.ExpectedStartDate, 126)
instead of just ExpectedStartDate
EDIT
In response the comment:
Try getting rid of
convert(varchar(7), w.ExpectedStartDate, 126) AS StatusOfMonth
and just try it like this:
SELECT
convert(varchar, datepart(yyyy, w.ExpectedStartDate)) + '-' + CONVERT(varchar(3), DATENAME(month, w.ExpectedStartDate)),
w.StatusTypeId,
st.StatusTypeName,
COUNT(w.StatusTypeId) AS StatusCount
FROM
Table1 w LEFT OUTER JOIN
StatusType st ON st.StatusTypeId = w.StatusTypeId
WHERE
w.ExpectedStartDate BETWEEN '1/10/2011' AND '04/14/2011'
GROUP BY
datepart(M, w.ExpectedStartDate),
datepart(yyyy, w.ExpectedStartDate),
w.StatusTypeId,
st.StatusTypeName

Resources