SQL - Sum of positive and negative numbers using subquery - sql

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.

Related

Multiple conditions in select statement based on values

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,

SQL Query SUM and Divide by Distinct Date Count

I assistance, I am looking for the sum of a data field and then want to divide it by the number of distinct dates in that field.
SUM(CASE WHEN dateResolved IS NOT NULL
THEN 1 ELSE 0
END) / DISTINCT(dateResolved) AvgPerDay
If there are 32 dates in dateResolved, with 5 distinct dates, I want it to return 6.4.
By default it does integer division you need :
SUM(CASE WHEN dateResolved IS NOT NULL
THEN 1 ELSE 0
END) * 1.0 / COUNT(DISTINCT dateResolved) AvgPerDay
However simply count would also work :
COUNT(dateResolved) * 1.0 / COUNT(DISTINCT dateResolved) AvgPerDay
COUNT(dateResolved) will ignore null values.
I would do this as:
SUM(CASE WHEN dateResolved IS NOT NULL
THEN 1.0 ELSE 0
END) / COUNT(DISTINCT dateResolved) as AvgPerDay
But this is more simply phrased as:
COUNT(dateResolved) * 1.0 / COUNT(DISTINCT dateResolved) as AvgPerDay

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.

SQL find consecutive days of specific threshold reached

I have two columns; the_day and amount_raised. I want to find the count of consecutive days that at least 1 million dollars was raised. Am I able to do this in SQL? Ideally, I'd like to create a column that counts the consecutive days and then starts over if the 1 million dollar threshold is not reached.
What I've done thus far is create a third column that puts a 1 in the row if 1 million was reached. Could I create a subquery and count the consecutive 1's listed, then reset when it hits 0?
and here is the desired output
select dt,amt,
case when amt>=1000000 then -1+row_number() over(partition by col order by dt)
else 0 end col1
from (select *, sum(case when amt >= 1000000 then 0 else 1 end) over(order by dt) col
from t) x
Sample Demo
SELECT the_day,
amount_raised,
million_threshold,
CASE WHEN million_threshold <> lag_million_threshold AND million_threshold = lead_million_threshold
THEN 1
WHEN million_threshold = lag_million_threshold
THEN SUM(million_threshold) OVER ( ORDER BY the_day ROWS UNBOUNDED PRECEDING )
ELSE 0
END AS consecutive_day_cnt
FROM
(
SELECT the_day,
amount_raised,
million_threshold,
LAG(million_threshold,1) OVER ( ORDER BY the_day ) AS lag_million_threshold,
LEAD(million_threshold,1) OVER ( ORDER BY the_day ) AS lead_million_threshold
FROM
(
SELECT the_day,
amount_raised,
CASE WHEN amount_raised >= 1000000
THEN 1
ELSE 0
END AS million_threshold
FROM Yourtable
)
);

SQL inline variable assignment

I have a SQL statement that determines the number of days between two dates. If the difference is negative, the statement needs to select 0. This statement works but I was wondering if it is possible to assign the value of datediff("D",GETDATE(),dbo.tblKBX_Reward.validdate) to a # variable so I don't have to call it twice.
SELECT CASE
WHEN datediff("D",GETDATE(),dbo.tblKBX_Reward.validdate) < 0 THEN 0
ELSE datediff("D",GETDATE(),dbo.tblKBX_Reward.validdate)
END from ...
Please don't ask why I am calculating this value using SQL instead of in code.
Thanks
You could move the case statement inside the calculation, like this:
Select DateDiff("D", GetDate(), Case When YourColumn > GetDate()
Then YourColumn
Else GetDate()
End)
You can write a higher level SELECT something like this.
SELECT CASE
WHEN diff_date < 0 THEN 0
ELSE diff_date
END
FROM (
SELECT datediff("D",GETDATE(),dbo.tblKBX_Reward.validdate) AS diff_date
from ...
)
If you are selecting more than one row, then you can't assign it to a variable. In your case, you can use a derived table or a CTE:
SELECT Cols,
CASE
WHEN DiffDates < 0 THEN 0
ELSE DiffDates
END DiffDates
FROM ( SELECT Cols, datediff("D",GETDATE(),dbo.tblKBX_Reward.validdate) DiffDates
FROM dbo.tblKBX_Reward) A
Or:
;WITH CTE AS
(
SELECT Cols, datediff("D",GETDATE(),dbo.tblKBX_Reward.validdate) DiffDates
FROM dbo.tblKBX_Reward
)
SELECT Cols,
CASE
WHEN DiffDates < 0 THEN 0
ELSE DiffDates
END DiffDates
FROM CTE
SELECT
CASE WHEN daydiff < 0 THEN 0 ELSE daydiff END
FROM your tables/joins
CROSS APPLY (
SELECT DATEDIFF(DAY, GETDATE(), dbo.tblKBX_Reward.validdate)
) x (daydiff)
WHERE …
You could use a CTE:
WITH cte_dateDiffs (RewardId, dateDiff)
AS (
SELECT RewardId, datediff("D",GETDATE(), validdate) FROM dbo.tblKBX_Reward
)
SELECT CASE
WHEN dd.dateDiff < 0 THEN 0
ELSE dd.dateDiff
END
FROM dbo.tblKBX_Reward r
INNER JOIN cte_dateDiffs dd ON r.RewardId == dd.RewardId
...