SQL SUM() function ignoring WHERE clause and CASE statement - sql

SELECT u.FirstName + ' ' + u.LastName as 'User',
COUNT(*) as 'Number of Calls',
CONVERT(varchar(4), SUM(CASE WHEN c.FromTime BETWEEN #FromDate AND #ToDate
THEN DATEDIFF(mi, c.FromTime, c.ToTime) ELSE 0 END) / 60) + ':' +
CASE WHEN SUM(CASE WHEN c.FromTime BETWEEN #FromDate AND #ToDate
THEN DATEDIFF(mi, c.FromTime, c.ToTime) ELSE 0 END) % 60 < 10 THEN '0' ELSE '' END +
CONVERT(varchar(2), SUM(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60) as 'Total Time Spent',
CONVERT(varchar(4), AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) / 60) + ':' +
CASE WHEN AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60 < 10 THEN '0' ELSE '' END +
CONVERT(varchar(2), AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60) as 'Average Call Time'
FROM Calls c
JOIN Users u ON u.UserID = c.TakenBy
WHERE c.FromTime BETWEEN #FromDate AND #ToDate
GROUP BY u.UserID, u.FirstName, u.LastName
ORDER BY u.FirstName + ' ' + u.LastName
The preceding SQL query returns the correct "Number of Calls" but the "Total Time" and "Average Time" are always the same regardless of the # of calls (which is obviously wrong).
I've read and tried to implement using the CASE WHEN __ Then value ELSE 0 inside SUM but it still returns an incorrect value.
The only way I can get this query to return correct results is if I completely strip out all other info, e.g.
SELECT SUM(DATEDIFF(mi, FromTime, ToTime)) FROM Calls WHERE c.FromTime BETWEEN...
How can I still use my JOIN and GROUP BY and get the aggregate functions to give me the results I want?
Thanks for any and all help!

You're probably better off with a subquery, e.g. in the SELECT part, add something like (SELECT SUM(...) FROM ... WHERE ...) AS total_sum.

Related

How do I find the total of an alias and group is by another alias in SQL?

I am trying to find the sum of the field I have called total, in years, month, days, hours, minutes and group it by the field called Station.equipment.
SELECT a.Station_Name, c.Equipment_Name, CONCAT(a.Station_Name,' ',c.Equipment_Name) as Station_equipment, a.fail_from, a.fail_to,
+CASE
WHEN DATEPART(DAY, a.fail_to - a.fail_from) > 1
THEN CAST( DATEPART(DAY, a.fail_to - a.fail_from) - 1 AS nvarchar(100)) + ' Days '
ELSE ''
END
+ CASE
WHEN DATEPART(HOUR, a.fail_to - a.fail_from) > 0
THEN CAST( DATEPART(HOUR, a.fail_to - a.fail_from) AS nvarchar(100)) + ' Hours '
ELSE ''
END
+ CASE
WHEN DATEPART(MINUTE, a.fail_to - a.fail_from) > 0
THEN CAST( DATEPART(MINUTE, a.fail_to - a.fail_from) AS nvarchar(100)) + ' Minutes '
ELSE ''
END AS total
FROM vw_AllPotentialFailures_ByPeriod as a
LEFT JOIN EquipmentFailures as b
ON a.PotentialFailure_ID =b.PotentialFailure_ID
LEFT JOIN Equipment as c
ON b.Equipment_ID = c.Equipment_ID
WHERE fail_from between '14 Feb 2020' and '14 Feb 2023 23:59'
AND a.KPI_Applicable = 'LiftsNR'
order by 2, 3;
I've copied in the code that I've written so far, but haven't managed to figure out how to sum the data yet. I'm aware that the name 'Days' may be impacting my total but I would still like to keep this formatting. Any help would be greatly appreciated!

simplify a SQL case statement in a case expression

How would I simplify this case statement in T-SQL? It provides the desired result, but it's very unwieldy and hard to read. I have to use the inner case statement to convert a Julian date (aka 6 digit number) into a regular date format.
Basically i'm doing a datediff( getdate(), case statement). Getdate() just returns the time now (ie. 2/27/2020) and the case statement converts a julian date (ie. 123456) into a normal date (ie, 1/1/2020).
Here's the expect output if the query was ran today on Feb 27.
Select CASE
WHEN Datediff(day, Getdate(), CASE
WHEN a.wadpl = 0
THEN NULL
ELSE Dateadd(d, Substring(Cast(wadpl AS VARCHAR(6)), 4, 3) - 1, CONVERT(DATETIME, CASE
WHEN LEFT(Cast(wadpl AS VARCHAR(6)), 1) = '1'
THEN '20'
ELSE '21'
END + Substring(Cast(wadpl AS VARCHAR(6)), 2, 2) + '-01-01'))
END) < 0
THEN 'Overdue Now'
WHEN Datediff(day, Getdate(), CASE
WHEN a.wadpl = 0
THEN NULL
ELSE Dateadd(d, Substring(Cast(wadpl AS VARCHAR(6)), 4, 3) - 1, CONVERT(DATETIME, CASE
WHEN LEFT(Cast(wadpl AS VARCHAR(6)), 1) = '1'
THEN '20'
ELSE '21'
END + Substring(Cast(wadpl AS VARCHAR(6)), 2, 2) + '-01-01'))
END) <= 30
THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM Table_X
Here is a easy one to understand, assuming a.wadpl is an integer:
SELECT CASE
WHEN DATEDIFF(DAY, GETDATE(), DATEADD(DAY, a.wadpl % 1000, DATEADD(YEAR,a.wadpl / 1000,'1899-12-31'))) <0 THEN 'Overdue now'
WHEN DATEDIFF(DAY, GETDATE(), DATEADD(DAY, a.wadpl % 1000, DATEADD(YEAR,a.wadpl / 1000,'1899-12-31'))) <= 30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM Table_X
or you can simplify by using a subquery (or you can use a WITH):
SELECT CASE
WHEN Age <0 THEN 'Overdue now'
WHEN Age <= 30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM (
SELECT DATEDIFF(DAY,GETDATE(),
DATEADD(DAY,wadpl%1000,DATEADD(YEAR,wadpl/1000,'1899-12-31'))) Age, *
FROM Table_X) a
This will of course cause you to do this arithmetic for each row, and you can't easily use any indexes. If you were asking about aggregates, then I would suggest doing the opposite, and pre-calculating the dates and use those in your query instead. You might also want to consider putting a persisted computed column on table_x:
ALTER TABLE TABLE_X
ADD wadpl_dt AS
(DATEADD(DAY,wadpl%1000,DATEADD(YEAR,wadpl/1000,'1899-12-31'))) PERSISTED;
Now you can just refer to table_x.wadpl_dt whenever you want the datetime, and your query would become:
SELECT CASE
WHEN Age <0 THEN 'Overdue now'
WHEN Age <= 30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM (
SELECT DATEDIFF(DAY,GETDATE(), wadpl_dt) Age, *
FROM Table_X) a
Here is the easy way to convert a date to what you refer to as the julian date:
SELECT (DATEPART(YEAR,GETDATE())-1900) * 1000 + DATEPART(DAYOFYEAR, GETDATE())
And this is how you can use it:
DECLARE #overdue int;
DECLARE #next30 int;
SET #overdue = (SELECT (DATEPART(YEAR,GETDATE())-1900) * 1000 + DATEPART(DAYOFYEAR, GETDATE()));
SET #next30 = (SELECT (DATEPART(YEAR,GETDATE()+30)-1900) * 1000 + DATEPART(DAYOFYEAR, GETDATE()+30));
SELECT CASE
WHEN wadpl < #overdue THEN 'Overdue now'
WHEN wadpl <= #next30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM Table_X

How to join multi tables with case statement?

How to get data from multiple tables with case on one table, I tried like below but getting error.
select login.UserNname,LastLoggedInDetails.LoggedInIP,UserType.UserType,LastLoggedInDetails.LoggedInTime,
LastLoggedInDetails.LoggedOutTime,LastLoggedInDetails.LoggedInVersion,CompanyRegistered.Comp_Name,
case
when LastLoggedInDetails.LoggedOutTime is not null then CONVERT(VARCHAR(100),DATEDIFF (hour,LastLoggedInDetails.LoggedInTime ,LastLoggedInDetails.LoggedOutTime ))+' Hours '+
CONVERT(VARCHAR(100),DATEDIFF (minute,LastLoggedInDetails.LoggedInTime ,LastLoggedInDetails.LoggedOutTime )%60) +' Minutes' as Duration
when LastLoggedInDetails.LoggedOutTime is null then 'Running...' as Duration
end
from LastLoggedInDetails
join login on LastLoggedInDetails.LastLoggedUserId = login.RegistrationId
join UserType on LastLoggedInDetails.LastLoggedUserTypeId = UserType.UserTypeId
join CompanyRegistered on LastLoggedInDetails.RegCompanyId = CompanyRegistered.Comp_Id
Here is your code for the case, formatted to be helpful:
case when LastLoggedInDetails.LoggedOutTime is not null
then CONVERT(VARCHAR(100),
DATEDIFF(hour, LastLoggedInDetails.LoggedInTime, LastLoggedInDetails.LoggedOutTime)
) + ' Hours ' +
CONVERT(VARCHAR(100),
DATEDIFF(minute, LastLoggedInDetails.LoggedInTime,
LastLoggedInDetails.LoggedOutTime
)%60
) +' Minutes' as Duration
--------------------------------^
when LastLoggedInDetails.LoggedOutTime is null
then 'Running...' as Duration
end
Formatting the code makes the problem obvious: case is an expression that can be named. The individual results from each then are not named. So, try this:
(case when LastLoggedInDetails.LoggedOutTime is not null
then CONVERT(VARCHAR(100),
DATEDIFF(hour, LastLoggedInDetails.LoggedInTime, LastLoggedInDetails.LoggedOutTime)
) + ' Hours ' +
CONVERT(VARCHAR(100),
DATEDIFF(minute, LastLoggedInDetails.LoggedInTime,
LastLoggedInDetails.LoggedOutTime
)%60
) +' Minutes'
when LastLoggedInDetails.LoggedOutTime is null
then 'Running...'
end) as Duration
select login.UserNname,LastLoggedInDetails.LoggedInIP,UserType.UserType,LastLoggedInDetails.LoggedInTime,
LastLoggedInDetails.LoggedOutTime,LastLoggedInDetails.LoggedInVersion,CompanyRegistered.Comp_Name,
case
when LastLoggedInDetails.LoggedOutTime is not null then CONVERT(VARCHAR(100),DATEDIFF (hour,LastLoggedInDetails.LoggedInTime ,LastLoggedInDetails.LoggedOutTime ))+' Hours '+
CONVERT(VARCHAR(100),DATEDIFF (minute,LastLoggedInDetails.LoggedInTime ,LastLoggedInDetails.LoggedOutTime )%60) +' Minutes'
when LastLoggedInDetails.LoggedOutTime is null then 'Running...'
end as Duration
from LastLoggedInDetails
join login on LastLoggedInDetails.LastLoggedUserId = login.RegistrationId
join UserType on LastLoggedInDetails.LastLoggedUserTypeId = UserType.UserTypeId
join CompanyRegistered on LastLoggedInDetails.RegCompanyId = CompanyRegistered.Comp_Id

COALESCE not working?

I have NULLS in my P.EMP_PAY_DUE_TO_LEAVE_DATE field and I've tried eliminating them by using the following bit of code but the NULLS remain!
SELECT DISTINCT V.EMP_CODE
, CASE
WHEN V.HIST_PERIOD < 10
THEN
CAST(V.HIST_YEAR AS VARCHAR) + RIGHT('0' + CAST (V.HIST_PERIOD + 3 AS VARCHAR), 2)
ELSE
CAST(V.HIST_YEAR + 1 AS VARCHAR) + RIGHT('0' + CAST (V.HIST_PERIOD - 9 AS VARCHAR) ,2)
END AS PAYPERIOD
, V.Department_Id
, CASE
WHEN MONTH (P.EMP_PAY_DUE_TO_LEAVE_DATE) >= MONTH(DATEADD(M, -1, GETDATE())
OR P.EMP_PAY_DUE_TO_LEAVE_DATE IS NULL
THEN
COALESCE
(
CONVERT
(
DECIMAL(10, 2)
, V.EMP_SORT_DESC
)
, 0
) / 37.5
END AS FTE
I can't see anything wrong with my code but clearly there is! Can anyone see the problem?
Your last...
CASE
WHEN (MONTH (P.EMP_PAY_DUE_TO_LEAVE_DATE) >= MONTH(DATEADD(M, -1, GETDATE())) OR P.EMP_PAY_DUE_TO_LEAVE_DATE IS NULL)
THEN (COALESCE (CONVERT (DECIMAL (10, 2) ,V.EMP_SORT_DESC), 0) / 37.5) END AS FTE
does not have an ELSE case, which implicity becomes NULL if the THEN case doesn't apply.

Join 2 queries with equal fields created dynamically

I have a table that i wanted to create some charts.
Basically i need to know how many registers i have in interval of 10 minutes and how many of them are already pos-processed. With this 2 queries i have the info i need, but i need they 2 in just 1 result table.
First query
SELECT
(
CAST(DATEPART(HOUR, m.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, m.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Movimentações
FROM
Integracao m
WHERE
m.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND m.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
ORDER BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
Second query
SELECT
(
CAST(DATEPART(HOUR, s.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, s.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Sucesso
FROM
Integracao s
WHERE
s.Veiculo_Modelo <> ''
AND s.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND s.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, s.Ocr_DataHora),
(DATEPART(MINUTE, s.Ocr_DataHora) % 6)
How can i join them if my common field is generated dynamically?
As others have already told you, you could just use the two queries as derived tables (subselects) and join them using each one's Hora column. It doesn't matter if the column is computed in the query, it's perfectly legal to use it in a join condition.
However, I think you can get the same results more efficiently, by calculating both counts in a single query (i.e. without subqueries). Here's how you can do that:
SELECT
(
CAST(DATEPART(HOUR, m.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, m.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Movimentações,
COUNT(CASE WHEN s.Veiculo_Modelo <> '' THEN 1 END) AS Sucesso
FROM
Integracao m
WHERE
m.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND m.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
ORDER BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
As you can see, the query doesn't filter rows on s.Veiculo_Modelo <> ''. However, that condition is used when counting rows for the Sucesso column. The argument of the second COUNT is a CASE expression. It returns a value (arbitrarily chosen to be 1, but could be anything really) if the specified condition is met and NULL otherwise. Since COUNT omits nulls, the result will be the same as with your dedicated query calculating and returning Sucesso.
I am assuming that you want the output to be as follows:
Hora | Movimentações | Sucesso
You can use the two SQL queries as if they are table using the WITH statement as follows:
WITH
firstQuery(Hora, Movimentações) AS (<first query definition with out ORDER BY >),
secondQuery(Hora, Sucesso) AS (<second query definition with out ORDER BY >)
SELECT
f.Hora, f.Movimentações , s.Sucesso
FROM
firstQuery f
JOIN secondQuery s
ON f.Hora = s.Hora
You should be able to do this by putting them in a sub query in a from clause. The way I'm referring to has the following form:
Select A.Hora,A.Movimentações,B.Sucesso
From (*First Query*) as A
inner join (*Second Query*) as B on A.Hora = B.Hora
The end result being:
Select A.Hora,A.Movimentações,B.Sucesso
From (SELECT
(
CAST(DATEPART(HOUR, m.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, m.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Movimentações
FROM
Integracao m
WHERE
m.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND m.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
ORDER BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)) As A
inner join (SELECT
(
CAST(DATEPART(HOUR, s.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, s.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Sucesso
FROM
Integracao s
WHERE
s.Veiculo_Modelo <> ''
AND s.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND s.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, s.Ocr_DataHora),
(DATEPART(MINUTE, s.Ocr_DataHora) % 6)) As B on B.Hora = A.Hora