Float value using case in SQL Server - sql

I have this code and I'm trying to compare [Dis] to [Estimator] which is a variable I am adding:
SELECT
[Id],
[Sym],
[Month],
[Dis],
Estimator = 4/count(Id) over (partition by Sym, Month)
FROM
[RegularPayResultsYearly]
Again, I'm trying to compare Dis to Estimator.
For example, the count(Id) over (partition by Sym, [Month]) is 12091 observations.
The problem : while the Dis column is a float type (example values :
8.2944916060179E-06, 9.07691249104339E-05) it seems that the
4/count(Id) over (partition by Sym, [Month]) as cnt
which is 3.308245802663138e-4 cannot go further than zero and it showing me
Estimator = 0.
Any way to change that?
Thanks.

Unfortunately, you cannot compare floating point times using equality -- and be consistently accurate. You can have two solutions. One is to take the absolute value of the difference and call it 0 if it is less than some (arbitrary) threshold:
SELECT . . .,
(CASE WHEN ABS(Dis) - 4/count(Id) over (partition by Sym, Month)) < 0.001
THEN 'EQUAL'
ELSE 'NOTEQUAL'
END)
FROM RegularPayResultsYearly
The second is to use decimal/numeric rather than float:
SELECT . . .,
(CASE WHEN CAST(Dis, DECIMAL(10, 3)) =
CAST(4.0/count(Id) over (partition by Sym, Month) AS DCIMAL(10, 3) = 0
THEN 'EQUAL'
ELSE 'NOTEQUAL'
END)
FROM RegularPayResultsYearly
Do note that the expression 4/COUNT(ID) OVER (PARTITiON BY Sym, Month) is going to return an integer (and probably 0 at that). SQL Server does integer division when both operands are integers.

In the below code, IN ELSE, what if I dont want to pass 'NOTEQUAL' and pass a NULL Value of Float
SELECT . . .,
(CASE WHEN CAST(Dis, DECIMAL(10, 3)) =
CAST(4.0/count(Id) over (partition by Sym, Month) AS DCIMAL(10, 3) = 0
THEN 'EQUAL'
ELSE 'NOTEQUAL'
END)
FROM RegularPayResultsYearly

Related

Calculate rate based on condition using postgresql

I want to find the rate of negative and zero profits from a column. I tried to do it using aggregate and subquery but it doesn't seem to work as both method return 0 values.
The code is as follows
SELECT
COUNT(CASE WHEN profit < 0 THEN 1
END) AS negative_profits,
COUNT(CASE WHEN profit < 0 THEN 1
END) / COUNT(profit),
COUNT(CASE WHEN profit = 0 THEN 1
END) AS zero_profits,
COUNT(CASE WHEN profit = 0 THEN 1
END) / COUNT(profit)
FROM sales;
SELECT (SELECT COUNT(*)
FROM sales
WHERE profit <= 0)/COUNT(profit) AS n_negative_profit
FROM sales;
Both query return 0 in values
enter image description here
Avoid integer division, which truncates (like Adrian pointed out).
Also, simplify with an aggregate FILTER expression:
SELECT count(*) FILTER (WHERE profit <= 0)::float8
/ count(profit) AS n_negative_profit
FROM sales;
If profit is defined NOT NULL, or to divide by the total count either way, optimize further:
SELECT count(*) FILTER (WHERE profit <= 0)::float8
/ count(*) AS n_negative_profit
FROM sales;
See:
Aggregate columns with additional (distinct) filters
Because you are doing integer division per docs Math operators/functions.
numeric_type / numeric_type → numeric_type
Division (for integral types, division truncates the result towards zero)
So:
select 2/5;
0
You need to make one of the numbers float or numeric:
select 2/5::numeric;
0.40000000000000000000
and to make it cleaner round:
select round(2/5::numeric, 2);
0.40

How to present a particular SQL queried row as columns in output

I need to present the attached output in PIC1 as the result in PIC2. The query used for generating PIC1 output in SQLDeveloper:
select subs_nm, as_of_date, run_status, (select max (tp.pr_vl)
from ual_mng.tqueue tq, ual_mng.tparams tp, ual_mng.tstatus ts
WHERE tq.tid = tp.tid AND tq.tid = ts.tid and tq.run_id = pcm.run_id and tp.pr_nm in ('TOT_RECORD_CNT')) as RECORD_COUNT
from UAL_MNG.PCM_SUBS_RUN_DTL_VW pcm where SUBS_NM='S_TS2_AQUA_A1_RLAP_DL' and AS_OF_DATE in ('2021-09-01','2021-09-02') order by run_start_dtm desc;
Appreciate all help.
If you don't need it to be dynamic (ie. it will only be two columns and you know which two months they are) you can do
select subs_nm,
max(case when as_of_date = '2021-09-01' then RECORD_COUNT else 0 end) as SEP1,
max(case when as_of_date = '2021-09-02' then RECORD_COUNT else 0 end) as SEP2,
from (
-- Your query
)
group by subs_nm
You can work out the percentage difference using the same expressions.
nb. I would always use an explicit date format mask. This might not run on a different machine / software. So use to_date('2021-09-01', 'yyyy-mm-dd')
Posting the query, which worked in the script :
select subs_nm, SEP1, SEP2, round((((SEP1-SEP2)/SEP1)*100),2) as DIFF_PER from ( select subs_nm,
max(case when as_of_date='2021-09-01' then RECORD_COUNT else '0' end) as SEP1,
max(case when as_of_date='2021-09-02' then RECORD_COUNT else '0' end) as SEP2 from (-- *Main Query*);

Doing math on sql count

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

SQL Exclusion in the select query

I have a SQL Server query which is processing several thousands of rows. However the script runs fine, but I need to apply to just one of the select statements a criteria but not have it affect the rest of the results.
Select
Count ([key]) as KEYCOUNT,
Round (AVG ([AGE]),2) as AGE,
Round (AVG ([LENGTH]),2) as Length_X,
Round (AVG ([Duration]),2) as DUR_Y,
from
[dbo].[XYZ]
where
[FLAG] = 1 and STAT = 3
The select I need to affect is Round (AVG ([LENGTH]),2) as Length_X, which I need to calculate as the average of only those lengths which are greater than 0, basically excluding all 0 (zeros)
Help much appreciated
Cheers
You can use a case expression as the parameter of that AVG function:
Round(AVG(case when [LENGTH] > 0 then [LENGTH] end), 2) as Length_X,
This way all 0 values will be ignored by the AVG, while all other expressions in your query won't be affected.
which I need to calculate as the average of only those lengths which are greater than 0, basically excluding all 0 (zeros)
Which is just sum(Length) / sum(case Length when 0 then 0 else 1 end) with the appropriate casting if needed. E.g.
set nocount on;
with cte as (select * from (values (0), (1), (2), (1), (0), (0)) as l(length))
select avg(cast(length as float)) as avg1,
sum(cast(length as float)) / sum(case length when 0 then 0 else 1 end) as avg2
from cte;
with cte as (select * from (values (1), (2), (1) ) as l(length))
select avg(cast(length as float)) as avg1,
sum(cast(length as float)) / sum(case length when 0 then 0 else 1 end) as avg2
from cte;
Notice the case condition. I used "not zero", you said "greater than zero". You choose.

Calculate running balance

I found below query to calculate running balance.
SELECT intId, varName, decAmt, charCrDr,
SUM(CASE WHEN charCrDr = 'c' THEN
decAmt
ELSE
decAmt * -1
END )
OVER (PARTITION BY varName ORDER BY intId) As decTotal
FROM #Temp;
I would like to know what it means by else condition
This is a crude way of doing the SUM OR DIFFERENCE operation:
Here what you are trying to do is to find the difference for decAmt like below:
decAmt(where charCrDr = 'c') - decAmt
decAmt * -1 multiplies the value of decAmt by -1 so that if it is positive it becomes negative number if it is negative it becomes positive number
Actually it's about the Credit(Cr) and Debit(Dr). The running balance is:
sum of Credits - (sum of Debits)
So the decAmt for debit records (in the ELSE part, that is WHEN charCrDr != 'c') are multiplied by -1 to maintain the above formula.
Good Luck.