Sum negative and positive numbers - sql

I'm using SQL Server 2008. I've got sales for customers and I want to compare it to what the customer bought the previous year, also calculate the growth. It all works fine but as soon as the customer has a negative for one of the months it brings back the wrong data for Target & Growth.
Customer SalesLastYearMonth SalesThisYearMonth Target Growth
------------------------------------------------------------------------------
abcd -1 15 ???? ???
Code:
SELECT
Customer,
CASE
WHEN SalesThisYearMonth IS NULL THEN (SalesLastYearMonth * -1)
WHEN SalesLastYearMonth IS NULL THEN SalesThisYearMonth
ELSE SalesThisYearMonth - SalesLastYearMonth END as Target,
CASE
WHEN SalesThisYearMonth IS NULL THEN -1
WHEN SalesLastYearMonth IS NULL THEN 1
WHEN SalesThisYearMonth = 0 then -1
WHEN SalesLastYearMonth = 0 then 1
ELSE ( SalesThisYearMonth - SalesLastYearMonth) / SalesLastYearMonth END AS Growth

Assuming you want TARGET=16, GROWTH=16, then this should do it:
SELECT
Customer,
CASE
WHEN SalesThisYearMonth IS NULL THEN (SalesLastYearMonth * -1)
WHEN SalesLastYearMonth IS NULL THEN SalesThisYearMonth
ELSE SalesThisYearMonth - SalesLastYearMonth END as Target,
CASE
WHEN SalesThisYearMonth IS NULL THEN -1
WHEN SalesLastYearMonth IS NULL THEN 1
WHEN SalesThisYearMonth = 0 then -1
WHEN SalesLastYearMonth = 0 then 1
ELSE ( SalesThisYearMonth - SalesLastYearMonth) / ABS(SalesLastYearMonth) END AS Growth
However, I'm really not sure about GROWTH there - not sure why you're dividing by SalesLastYearMonth. In the case above this works, but if SalesLastYearMonth is -4, is dividing by 4 what you want?

You are dividing by a negative number:
SalesLastYearMonth = -1
You need to either check for it or return the absolute value.

Just a little suggestion. try using the ISNULL() function. if the value you select is NULL then you can give a default return value. As far as i know its much faster then CASE WHEN.

Related

Overflow error when using the sum function?

I'm writing a query to calculate how much more the average customer has transacted and spent since signing up with my company. However, it returns the error message "Overflow occurred during numeric data type conversion".
The problem is definitely this bit, as when I comment it out the query runs successfully:
SUM(SALES_GROWTH_PERCENT)
Google tells me it's likely that I'm trying to fit a number with too many decimal places into a field that's too short for it. However, I've tried changing how many decimal places the case statements are using with no luck.
Edit: for context, here are the details of the fields being used, from syscat.columns:
This is the query in question. Can anyone see where I'm going wrong please? Any advice would be appreciated!
SELECT SUM(TRANS_GROWTH_PERCENT)/COUNT(CUSTOMER_ID) AS AVG_TRANS_GROWTH_PERCENT,
SUM(SALES_GROWTH_PERCENT)/COUNT(CUSTOMER_ID) AS AVG_SALES_GROWTH_PERCENT
FROM
(
SELECT CUSTOMER_ID,
CASE WHEN POST_SIGNUP_TRANS <=0
THEN 0
WHEN POST_SIGNUP_TRANS >0
THEN CAST((CAST(POST_SIGNUP_TRANS AS DECIMAL (10,5)) - CAST(PRE_SIGNUP_TRANS AS DECIMAL (10,5))) / CAST(PRE_SIGNUP_TRANS AS DECIMAL (10,5)) * 100 AS DECIMAL (10,1))
ELSE NULL
END AS TRANS_GROWTH_PERCENT,
CASE WHEN POST_SIGNUP_SALES <=0
THEN 0
WHEN POST_SIGNUP_SALES >0
THEN CAST((CAST(POST_SIGNUP_SALES AS DECIMAL (10,5)) - CAST(PRE_SIGNUP_SALES AS DECIMAL (10,5))) / CAST(PRE_SIGNUP_SALES AS DECIMAL (10,5)) * 100 AS DECIMAL (10,1))
ELSE NULL
END AS SALES_GROWTH_PERCENT
FROM
(
SELECT CUSTOMER_ID,
SUM(PRE_SIGNUP_TRANS) AS PRE_SIGNUP_TRANS, SUM(PRE_SIGNUP_SALES) AS PRE_SIGNUP_SALES,
SUM(POST_SIGNUP_TRANS) AS POST_SIGNUP_TRANS, SUM(POST_SIGNUP_SALES) AS POST_SIGNUP_SALES
FROM
(
SELECT CUSTOMER_ID,
CASE WHEN TRANSACTION_DATE >= ANALYSIS_START_DATE THEN 1 ELSE 0 END AS ANALYSIS_FLAG,
CASE WHEN TRANSACTION_DATE < SIGNUP_DATE THEN 1 ELSE 0 END AS PRE_SIGNUP_TRANS,
CASE WHEN TRANSACTION_DATE < SIGNUP_DATE THEN SALES ELSE 0 END AS PRE_SIGNUP_SALES,
CASE WHEN TRANSACTION_DATE >= SIGNUP_DATE THEN 1 ELSE 0 END AS POST_SIGNUP_TRANS,
CASE WHEN TRANSACTION_DATE >= SIGNUP_DATE THEN SALES ELSE 0 END AS POST_SIGNUP_SALES
FROM TRANSACTIONS_TABLE
)
WHERE ANALYSIS_FLAG = 1
GROUP BY CUSTOMER_ID
)
WHERE PRE_SIGNUP_TRANS >0
AND PRE_SIGNUP_SALES >0
)
;

BASIC SQL - Case expression - Modifying "Then 1 Else 0" To return count of rows where case statement is true

I am a sales professional interested in honing my SQL skills further. Stackoverflow has been a great resource for me thus far!
Goal:
I want to get a distinct count of all our customers by DMA (aka Metro U.S. Area) where the case expression below is true.
PROBLEM
Right now, when I execute the query, the resulting count for the "current month -1" column (which should return the count of all customers who match the case statement) is just returning a 1. I would like for this to return the count of o.intorgnodeID (customer IDS) in which the case expression was true (57 customers).
In other words, I suspect that the "then 1" part of the case statement is what is causing me issues. But I am not sure how to modify the "1' to count just the results of the original case statement
SELECT
o.strDMANode --- AKA Metro Market
,case when(sum(case when (year(getdate()) - 1) * 12 + month(getdate()) - ((year(sbi.dtmdelivered) - 1) * 12 + month(sbi.dtmdelivered)) = 24 then 1 else 0 end)) >0 then 1 else 0 end as 'Current Month - 1' --- this is the output column that I hope to have return a value of '57'. Currently is returning a '1'
FROM sqlfact.dbo.uvwreport as sbi
JOIN [sqlDim].[dbo].[uvwdimOrgNodeType1] o ON [sbi].[intDimOrgNodeID] = [o].[intDimOrgNodeID]
JOIN [sqlDim].[dbo].[uvwdimProductType1] as "z" ON [sbi].[intDimProductPrimaryID] = [z].[intDimProductID]
WHERE
([sbi].[intstatusid] = 5 OR sbi.intsubstatusid = 43) --- Includes only delivered reports
and [sbi].[mnyDollarcost] > 0 --- NO $0 reports
and [o].[bitCurrent] = 1 --- Excludes all historical versions of OrgNode, which were duplicates
and [o].[strSalesRegionNodeGroup] = 'Construction'
GROUP BY [o].[strDMANode]
ORDER BY [o].[strDMANode] asc
CURRENT RESULTS:
strDMANode Result
ABILENE-SWEETWATER DMA 1
DESIRED RESULTS:
strDMANode Column Result
ABILENE-SWEETWATER DMA 57
I think you want the case as an argument to sum():
sum(case when(case when (year(getdate()) - 1) * 12 + month(getdate()) - ((year(sbi.dtmdelivered) - 1) * 12 + month(sbi.dtmdelivered)) = 24 then 1 else 0 end)) >0 then 1 else 0 end) as 'Current Month - 1'

How to calculate percentage for sum of difference of two columns in sql

I have two columns in the same table, that needs to be summed and get the difference and then calculate the percentage?
SELECT sum([BillCount]) - sum([IssueCount]) as [Difference]
FROM Invoice
where [Year] = '2015'
In the above query I got the result for the difference as 244234. Now I need to calculate the percentage by dividing the difference with the actual Billcount 260918.
sample Data:
SELECT sum(BillCount) as bills, sum(IssueCount) as issues
FROM Invoice
where [Year] = '2015'
Result:
Bills: 260918
Issues: 16684
Difference: 244234
Divide Difference By actual Bills: 244234/260918 = 0.93
Percentage: 0.93*100 = 93.60
I have used the changed divisor from zero to one. But I am getting the percentage result in many rows, like for each individual billcount. Is there any way that I could get the exact percentage in one row like we calculate in calculator?
Thanks.
Like this?
SELECT sum([BillCount]) - sum([IssueCount]) as [Difference],
(sum(IssueCount) - sum(BillCount)) / nullif(sum(BillCount), 0) as ptcdiff
FROM Invoice
where [Year] = '2015'
You'll note I changed the order of subtraction. This makes it so that its a negative change rather than a positive change. If you need an absolute. change, you can just throw abs() around the result. You can also bake this into a function if it makes sense to
declare
#pctOld float = 260918,
#pctNew float = 16684
select PctDiff = case when #PctNew is null or #PctOld is null then null
when #PctNew = null or #PctOld = null then null
when #PctNew = 0 and #PctOld = 0 then 0
when #PctNew = 0 and #PctOld > 0 then -100.00
when #PctNew >= 0 and #PctOld < 0 then null
when #PctNew < 0 and #PctOld > 0 then null
when #PctOld = 0 then null
else ((#PctNew - #PctOld) / abs(#PctOld)) * 100.0
end
Subquery like this
Select Difference/nullif(sum(BIllCount), 1) From (SELECT sum([BillCount]) - sum([IssueCount]) as [Difference],
FROM Invoice
where [Year] = '2015') Per
Divide your current expression with SUM([BillCount]) aggregated value
Something like this
select (sum([BillCount]) - sum([IssueCount])) / NULLIF(sum([BillCount]),0) * 100
FROM Invoice
where [Year] = '2015'

T-SQL Sum Case Confusion

I am currently doing a SUM CASE to workout a percentage, however the entire string returns zero's or ones (integers) but I don't know why. I have written the SQL in parts to break it out and ensure the underlying data is correct which it is, however when I add the last part on to do the percentage it fails. Am I missing something?
SELECT
SUPPLIERCODE,
(SUM(CASE WHEN ISNULL(DATESUBMITTED,0) - ISNULL(FAILDATE,0) <15 THEN 1 ELSE 0 END)) AS ACCEPTABLE,
COUNT(ID) AS TOTALSUBMITTED,
(SUM(CASE WHEN ISNULL(DATESUBMITTED,0) - ISNULL(FAILDATE,0) <15 THEN 1 ELSE 0 END)/COUNT(ID))
FROM SUPPLIERDATA
GROUP BY SUPPLIERCODE
For example here's some of the data returned:
SUPPLIERCODE ACCEPTABLE TOTALSUBMITTED Column1
HBFDE2 1018 1045 0
DTETY1 4 4 1
SWYTR2 579 736 0
VFTEQ3 2104 2438 0
I know I could leave the other columns and use an excel calculation but I'd rather not... Any help would be well received. Thanks
SELECT
SUPPLIERCODE,
(SUM(CASE WHEN ISNULL(DATESUBMITTED,0) - ISNULL(FAILDATE,0) <15 THEN 1 ELSE 0 END)) AS ACCEPTABLE,
COUNT(ID) AS TOTALSUBMITTED,
(SUM(CASE WHEN ISNULL(DATESUBMITTED,0) - ISNULL(FAILDATE,0) <15 THEN 1 ELSE 0 END)*1.0/COUNT(ID))
FROM SUPPLIERDATA
GROUP BY SUPPLIERCODE
You need convert your result to float. It can be easy done by multiply on 1.0
This is due to the fact that SQL Server is treating your values as INTs for the purpose of division.
Try the following and you will see the answer 0:
PRINT 1018 / 1045
In order to allow your operation to work correctly, you need to convert your values to FLOATs, like so:
PRINT CAST(1018 AS FLOAT) / 1045
This will produce the answer 0.974163 as expected.
A simple change to your statement to introduce a cast to FLOAT will sort your problem:
SELECT
SUPPLIERCODE,
(SUM(CASE WHEN ISNULL(DATESUBMITTED,0) - ISNULL(FAILDATE,0) <15 THEN 1 ELSE 0 END)) AS ACCEPTABLE,
COUNT(ID) AS TOTALSUBMITTED,
(CAST(SUM(CASE WHEN ISNULL(DATESUBMITTED,0) - ISNULL(FAILDATE,0) <15 THEN 1 ELSE 0 END) AS FLOAT) / COUNT(ID))
FROM SUPPLIERDATA
GROUP BY SUPPLIERCODE
All you have to do is avoid integer division by giving your database engine a hint.
In SQL Server, you would use:
SELECT
SUPPLIERCODE,
(SUM(CASE WHEN ISNULL(DATESUBMITTED, 0) - ISNULL(FAILDATE, 0) < 15 THEN 1 ELSE 0 END)) AS ACCEPTABLE,
COUNT(ID) AS TOTALSUBMITTED,
(SUM(CASE WHEN ISNULL(DATESUBMITTED, 0) - ISNULL(FAILDATE, 0) < 15 THEN 1 ELSE 0 END) / (COUNT(ID) * 1.0))
FROM
SUPPLIERDATA
GROUP BY
SUPPLIERCODE

YTD budget table to current period in SQL

I have a table in the following format (pseudo code):
CREATE TABLE [GLM_MASTER__BUDGET](
[Budget_Year],
[Budget_Type],
[Account],
[Period_1_Budget],
[Period_2_Budget],
[Period_3_Budget],
[Period_4_Budget],
[Period_5_Budget],
[Period_6_Budget],
[Period_7_Budget],
[Period_8_Budget],
[Period_9_Budget],
[Period_10_Budget],
[Period_11_Budget],
[Period_12_Budget]
)
I need to be able to query this with a with of a int for the current period like 6
This should return the the combined values of all periods from 1 to the number specified.
So I would end up with an output like this:
Budget_Year, Budget Type, Account, (Sum of all periods from 1 to the period specified) as Amount
I am using MS SQL server so it would be ok at the top to do something like:
DECLARE #current_period INT = 6
If you have the opportunity to redesign this table you may wish to consider it now.
You could use something along these lines:
DECLARE #current_period INT = 6
SELECT
Budget_Year, Budget Type, Account,
CASE WHEN #current_period >= 1 THEN [Period_1_Budget] ELSE 0 END +
CASE WHEN #current_period >= 2 THEN [Period_2_Budget] ELSE 0 END +
CASE WHEN #current_period >= 3 THEN [Period_3_Budget] ELSE 0 END +
CASE WHEN #current_period >= 4 THEN [Period_4_Budget] ELSE 0 END +
.....
..
CASE WHEN #current_period >= 12 THEN [Period_12_Budget] ELSE 0 END As Amount
FROM [GLM_MASTER__BUDGET]
Also it's not clear from your description whether you require a SUM and GROUP BY so I've left it out.