Doing math on sql count - sql

I am trying to do some mathematical operations on the results of two queries.
This is my query:
select (x.failed/y.total) * 100 as failure_rate, x.failed, y.total
from
(
select count(*) as failed from status where cast(ins As Date) = cast(getDate() As Date) and fail_flg = 'Y'
) x
join
(
select count(*) as total from status where cast(ins As Date) = cast(getDate() As Date)
) y on 1=1
This is the result im getting back:
failure_rate failed total
0 1 2
I should have a failure rate of 50, where am I going wrong? I have a gut suspicion the problem is somewhere in my count(*)....do I need to cast this as a number somewhere?

SQL Server does integer arithmetic. Convert to a non-integer number. I do this as:
select (x.failed * 100.0 /y.total) as failure_rate, x.failed, y.total
I should add that I would write the query without subqueries:
select sum(case when fail_flag = 'Y' then 1 else 0 end) as failed,
count(*) as total,
avg(case when fail_flag = 'Y' then 100.0 else 0 end) as failed_rate
from status
where cast(ins As Date) = cast(getDate() As Date) ;
Normally, I would recommend not doing the cast() or any other function on a column. That precludes the use of indexes. However, SQL Server makes an exception for cast(as date), so your code is still index-safe (or "sargable" in the lingo of SQL Server).

Related

CASE WHEN condition with MAX() function

There are a lot questions on CASE WHEN topic, but the closest my question is related to this How to use CASE WHEN condition with MAX() function query which has not been resolved.
Here is some of my sample data:
date
debet
2022-07-15
57190.33
2022-07-14
815616516.00
2022-07-15
40866.67
2022-07-14
1221510.00
So, I want to all records for the last two dates and three additional columns: sum(sales) for the previous day, sum for the current day and the difference between them:
SELECT
[debet],
[date] ,
SUM( CASE WHEN [date] = MAX(date) THEN [debet] ELSE 0 END ) AS sum_act,
SUM( CASE WHEN [date] = MAX(date) - 1 THEN [debet] ELSE 0 END ) AS sum_prev ,
(
SUM( CASE WHEN [date] = MAX(date) THEN [debet] ELSE 0 END )
-
SUM( CASE WHEN [date] = MAX(date) - 1 THEN [debet] ELSE 0 END )
) AS diff
FROM
Table
WHERE
[date] = ( SELECT MAX(date) FROM Table WHERE date < ( SELECT MAX(date) FROM Table) )
OR
[date] = ( SELECT MAX(date) FROM Table WHERE date = ( SELECT MAX(date) FROM Table ) )
GROUP BY
[date],
[debet]
Further, of course, it informs that I can't use the aggregate function inside CASE WHEN. Now I use this combination: sum(CASE WHEN [date] = dateadd(dd,-3,cast(getdate() as date)) THEN [debet] ELSE 0 END). But here every time I need to make an adjustment for weekends and holidays. The question is, is there any other way than using 'getdate' in 'case when' Statement to get max date?
Expected result:
date
sum_act
sum_prev
diff
2022-07-15
97190.33
0.00
97190.33
2022-07-14
0.00
508769.96
-508769.96
You can use dense_rank() to filter the last 2 dates in your table. After that you can use either conditional case expression with sum() to calculate the required value
select [date],
sum_act = sum(case when rn = 1 then [debet] else 0 end),
sum_prev = sum(case when rn = 2 then [debet] else 0 end),
diff = sum(case when rn = 1 then [debet] else 0 end)
- sum(case when rn = 2 then [debet] else 0 end)
from
(
select *, rn = dense_rank() over (order by [date] desc)
from tbl
) t
where rn <= 2
group by [date]
db<>fiddle demo
Two steps:
Get the sums for the last three dates
Show the results for the last two dates.
Well, we could also get all daily sums in step 1, but we just need the last three in order to calculate the sums for the last two days, so why aggregate more data than necessary?
Here is the query. You may have to put the date column name in brackets in SQL Server, as date is a keyword in SQL.
select top(2)
date,
sum_debit_current,
sum_debit_previous,
sum_debit_current - sum_debit_previous as diff
(
select
date,
sum(debet) as sum_debit_current,
lag(sum(debet)) over (order by date) as sum_debit_previous
from table
where date in (select distinct top(3) date from table order by date desc)
group by date
)
order by date desc;
(SQL Server uses TOP(n) instead of standard SQL FETCH FIRST 3 ROWS and while SELECT DISTINCT TOP(3) date looks like "get the top 3 rows, then apply distinct on their date", it is really "apply distinct on the dates, then get the top 3" like in standard SQL.)

CASE is not working as expected with MAX function in select clause

I am using the following query to get records:
select
MAX(lng_linenumber) as lng_linenumber,
str_topic,
MAX(dte_expire) as dte_expire,
MAX(CASE WHEN CAST(dte_expire as date) >= CAST(getdate() as date) THEN 0 ELSE 1 END ) as Is_Expire
from table1
group by str_topic
order by lng_linenumber desc
OUTPUT:
What i am doing?
I am using case statement to get Is_Expired 0 OR 1, if expired then 1 otherwise 0. As you can see on highlighted row dte_expire is still not achieved, but Is_Expire value is 1 it should be 0.
Please help me where i am making mistake in query.
Thanks in advance.
You can try below way -
select lng_linenumber,str_topic,dte_expire,case when dte_expire >= CAST(getdate() as date) then 0 else 1 end as is_expired
from
(
select MAX(lng_linenumber) as lng_linenumber, str_topic,MAX(dte_expire) as dte_expire
from table1
group by str_topic
)A
Your max() aggregation function is causing to get isExpire = 1, since 1 is the max value of this column.
Use below query.
select t1.str_topic
, t1.lng_linenumber
, t2.dte_expire
, case when cast(t2.dte_expire as date) >= cast(getdate() as date) then 0 else 1 end) as is_Expire
from
(select
max(lng_linenumber) as lng_linenumber,
str_topic
from table1
group by str_topic) t1
inner join table1 t2 on t2.lng_linenumber = t1.lng_linenumber and t2.str_topic = t1.str_topic
order by t1.lng_linenumber desc

Using SUM SEC_TO_TIME in MariaDB

Reference from How to sum time using mysql
I want to SUM Field LogsFormatted.Late Every month with query :
SELECT
SUM(CASE
WHEN MONTH (LogsFormatted.DateIn) = 1
THEN SEC_TO_TIME( SUM( TIME_TO_SEC(LogsFormatted.Late)))
ELSE 0 END
) AS '1'
FROM
HrAttLogsFormatted AS LogsFormatted
But the result is
1111 - Invalid use of group function
Where is the problem with the query? resulting in an error output.. Thank you in advance
[EDIT-SOLVED] It's Solved with simply apply
Change format SUM at the beginning of the query
SEC_TO_TIME(SUM(
CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN
TIME_TO_SEC(LogsFormatted.Late) END)
) AS '1'
You don't need to call the sum() so many times. You can also move the case condition to the WHERE clause:
SELECT SUM(TIME_TO_SEC(lf.Late))
FROM HrAttLogsFormatted lf
WHERE MONTH(lf.DateIn) = 1 ;
If you want conditional aggregation, then do:
SELECT SUM(CASE WHEN MONTH(lf.DateIn) = 1 THEN TIME_TO_SEC(lf.Late) END)
FROM HrAttLogsFormatted lf;

Work out percentage count using SQL CASE statement

I have the following code which tells me which line items are in and out of SLA.
How can I turn that into a %, so for example when I add them together it will show 98% SLA Met.
,CASE
WHEN m.COMPLETED_DT is NULL THEN ''
WHEN m.COMPLETED_DT <= m.SLA_ADJUSTED_DT THEN 'SLA Met'
WHEN m.SLA_ADJUSTED_DT IS NULL THEN 'SLA Met'
ELSE 'SLA Missed' END AS "SLA Desc"
If I had the result already, I think it would look something like...
SELECT (count(*) * 100 / (select count(*) FROM testtable)) AS YesSLA
FROM testtable where SLA='Yes';
I am not sure how to integrate that with my current statement, I don't believe I can reference the AS SLA Desc in a new statement.
Does this do what you want?
select 100 * avg(case when m.completed_dt <= m.SLA_ADJUSTED_DT or m.SLA_ADJUSTED_DT is null
then 1.0 else 0
end)
from testtable
where SLA = 'Yes';
The code below calculates the % met SLA out of 100 by counting only values that met SLA and then dividing by the total opportunities.
DECLARE #Data TABLE (COMPLETED_DT DATETIME, SLA_ADJUSTED_DT DATETIME)
INSERT #Data VALUES ('5/5/2014', '5/6/2014'), ('5/6/2014', '5/6/2014'), ('5/7/2014', '5/6/2014')
SELECT
CONVERT(FLOAT, SUM(CASE WHEN COMPLETED_DT <= SLA_ADJUSTED_DT THEN 1 ELSE 0 END)) * 100 / COUNT(1) AS [% Met SLA]
FROM #Data
Output
% Met SLA
----------------------
66.6666666666667

Return 0 when there is no row returned

I have this query which returns Null, to show 0 i can use ISNULL on outer query around CAST, i don't know if it is better to use ISNULL in the inner query.
I have tried using ISNULL with inner query but it returns no rows instead of showing 0. I have tried removing group by clause but still same results.
SELECT CAST((SUM(q.AssingWithPO * 1.0) / SUM(q.TotalAssign * 1.0)) * 100
AS NUMERIC (10,2))
FROM
(
SELECT COUNT(DISTINCT a.AssignmentID) AS TotalAssign
,(SELECT COUNT(DISTINCT a2.AssignmentID)FROM Assignments a2
WHERE a2.PODeliveryDate <> '19001212'
AND a2.AssignmentID = a.AssignmentID ) AS AssingWithPO
FROM Assignments a
WHERE a.StaffID = 59
AND (a.CreatedDate BETWEEN '20130101' AND '20141231')
GROUP BY a.AssignmentID
)q;
ADDED
I have simplified this query, thanks to #Gordon
SELECT SUM(case when a.PODeliveryDate <> '19001212' then 1.0 else 0.0 end) / COUNT(*)) * 100 as AssignWithPO
FROM Assignments a
WHERE a.StaffID = 59 AND
a.CreatedDate BETWEEN '20130101' AND '20141231';
Now would it be okay to use ISNULL like that?
ISNULL((SUM(case when a.PODeliveryDate <> '19001212' then 1.0 else 0.0 end) / COUNT(*)) * 100 as AssignWithPO,0)
Execution Plan of both queries
You don't need the second level of subqueries. You can use conditional aggregation instead. I think the following will do what you want:
SELECT CAST((SUM(a.AssignWithPO * 1.0) / SUM(a.TotalAssign * 1.0)) * 100 as NUMERIC (10,2))
FROM (SELECT COUNT(*) AS TotalAssign,
SUM(case when a.PODeliveryDate <> '19001212' then 1 else 0 end) as AssignWithPO
FROM Assignments a
WHERE a.StaffID = 59 AND
a.CreatedDate BETWEEN '20130101' AND '20141231'
GROUP BY a.AssignmentID
) a;
I'm not 100% sure, because I don't understand the relationships between AssignmentId, StaffId, and CreatedDate, but my assumption is that the rows counted for AssignWithPO are subject to the same conditions as the TotalCount.
You don't need the count(distinct) because AssignmentId is necessarily unique because of the group by. Assuming there is no overlap between the values, you don't need the group by either, nor the outer query:
SELECT COUNT(*) AS TotalAssign,
SUM(case when a.PODeliveryDate <> '19001212' then 1.0 else 0.0 end) / COUNT(*) as AssignWithPO
FROM Assignments a
WHERE a.StaffID = 59 AND
a.CreatedDate BETWEEN '20130101' AND '20141231';