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.
Related
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
I have a multiple ctes. In my select statement I must filter values base on the conditions. This is my query.
SELECT roadName
,sum(roadLength) AS sumRoadLength
,avg(elevationDifference) AS eglAvgDepth
,avg(elevationDifference) AS pglAvgDepth
,
FROM cte3
GROUP BY roadName
ORDER BY roadName
Under "elevationDifference" there are lots of values ranging from -10 to +20 which are spread through "roadName". What i want to accomplished is that "eglAvgDepth" will return if all "elevationDifference" values are <0 and take the average. Same case with pglAvgDepth where values are >0.
I tried to add where statement but works only in eglAvgDepth
WHERE elevationDifference < 0
GROUP BY roadName
ORDER BY roadName
Just add a conditional expression:
avg(case when elevationDifference < 0 then elevationDifference end) as eglAvgDepth,
avg(case when elevationDifference > 0 then elevationDifference end) as pglAvgDepth,
EDIT:
You have phrased this that you want the value based on whether all the values are positive or negative. If so:
(case when max(elevationDifference) < 0 then avg(elevationDifference) end) as eglAvgDepth,
(case when max(elevationDifference) > 0 then avg(elevationDifference) end) as pglAvgDepth,
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
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
I have a table which contains positive and negative numbers. I have to find out sum of positive and negative numbers using sub query
Your question isn't very clear (no table nor column names given), nor is it clear why you need a subquery (never a good idea if it can be avoided). You can get the values that you want by use of the 'case' statement
The following counts the number of positive and negative values
select sum (case when acolumn >= 0 then 1 else 0 end) as positive,
sum (case when acolumn < 0 then 1 else 0 end) as negative
from table
whereas the following sums the number of positive and negative values
select sum (case when acolumn >= 0 then acolumn else 0 end) as positive,
sum (case when acolumn < 0 then acolumn else 0 end) as negative
from table
For the sum of the negative :
SELECT SUM(numberColumn) FROM tableFoo WHERE numberColumn < 0
For the sum of the positive:
SELECT SUM(numberColumn) FROM tableFoo WHERE numberColumn >= 0
To combine the two (with QUERY1 and QUERY2 being the two previous queries):
SELECT (QUERY1), (QUERY2)
select sum(case when a>=0 then a else 0 end) as positive,
sum(case when a<0 then a else 0 end) as negative
from a
By using CTE(Common table Expression) we can get the output.
;WITH Psum_CTE
AS
( SELECT SUM(num) AS PositiveSum
FROM sample
WHERE num>=0
)
,Nsum_CTE
AS
(
SELECT SUM(num) AS NegativeSum
FROM sample
WHERE num<0
)
SELECT PositiveSum,NegativeSum
FROM Psum_CTE,Nsum_CTE
SELECT (
(SELECT SUM(numberColumn) FROM tableFoo WHERE numberColumn < 0 ) -
(SELECT SUM(numberColumn) FROM tableFoo WHERE numberColumn > 0)
) AS totalCalculation
You can use sign to separate the values:
select Sum( ( Sign( n ) + 1 ) / 2 * n ) as PositiveSum,
Sum( -( Sign( n ) - 1 ) / 2 * n ) as NegativeSum
from YourTableOData;
Sign returns 1, 0 or -1 depending on the sign of the input value. A little arithmetic can convert that into 1 or 0 depending on the sign: ( Sign( n ) + 1 ) / 2 is 1 for all positive values, otherwise 0. Note that the check for negative values (( Sign( n ) - 1 ) / 2) returns -1 or 0, hence the negation (-) to avoid flipping the sign of the value that is being summed.