SQL - Can I calculate a new column based on another new one? - sql

I'm creating a query in which there is a new column calculated similar to the following:
CASE WHEN DATEDIFF(day,lastpurchase,getdate())< 31 AND
turnover >= 3000 THEN '500'
WHEN DATEDIFF(day,lastpurchase,getdate())< 31 AND
turnover < 3000 THEN '200'
ELSE '0' END as OfferAmountEuro,
This works fine. I now want to create another calculated field on the same table which uses the 'OfferAmountEuro' field in it's calculation. I tried something like this:
CASE WHEN DATEDIFF(day,lastpurchase,getdate())< 31 AND
turnover >= 3000 THEN '500'
WHEN DATEDIFF(day,lastpurchase,getdate())< 31 AND
turnover < 3000 THEN '200'
ELSE '0' END as OfferAmountEuro,
CASE WHEN OfferAmountEuro = 500 AND Currency ='USD' then '600'
WHEN OfferAmountEuro = 200 AND Currency ='USD' then '250'
ELSE '0' END as OfferAmountLocal
But I get an error stating that 'OfferAmountEuro' is an invalid column name. I assume this means that I can't use the newly calculated 'OfferAmountEuro' field in the calculation for 'OfferAmountLocal'?
The actual calculations for the 'OfferAmountEuro' field are much more complex and numerous than the above in reality and I'd rather not repeat each of these calcs for the 'OfferAmountLocal' field.
Does anybody have any suggestions for a quick way to use this 'OfferAmountEuro' field in the calcs for another new field?

Your column is not recognized, because of natural sql statement execution order. Read more here:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/70efeffe-76b9-4b7e-b4a1-ba53f5d21916/order-of-execution-of-sql-queries
If you're using SQL Server, you can use CROSS APPLY as a workaround.
SELECT CASE
WHEN D.OfferAmountEuro = 500 AND T.Currency ='USD' then '600'
WHEN D.OfferAmountEuro = 200 AND T.Currency ='USD' then '250'
ELSE '0'
END AS OfferAmountLocal
FROM YourTable AS T
CROSS APPLY (
SELECT CASE
WHEN DATEDIFF(DAY, T.LastPurchase, GETDATE()) < 31 AND T.TurnOver >= 3000 THEN '500'
WHEN DATEDIFF(DAY, T.LastPurchase, GETDATE()) < 31 AND T.TurnOver < 3000 THEN '200'
ELSE '0'
END
) AS D(OfferAmountEuro)

You can either put the original query in a WITH statement and reference it in a second query, or replace all of the logic in your first computed column anywhere you want to use it.
Something like:
WITH C1 AS (
SELECT *,
CASE WHEN DATEDIFF(day,lastpurchase,getdate())< 31 AND turnover >= 3000 THEN '500'
WHEN DATEDIFF(day,lastpurchase,getdate())< 31 AND turnover < 3000 THEN '200'
ELSE '0' END as OfferAmountEuro
FROM myTable
)
SELECT *,
CASE WHEN OfferAmountEuro = 500 AND Currency ='USD' then '600'
WHEN OfferAmountEuro = 200 AND Currency ='USD' then '250'
ELSE '0' END as OfferAmountLocal
FROM C1

Related

sql nested "sum (case when) or" select query

I have a select query where I am trying to sum a value based on multiple criteria, grouped by dimension. The output will be the sum of the time greater than 30, 90, or 365 days based on the category, grouped by category. I can't crack the syntax for this, I hope you can help!
The concept looks something like this:
select
CATEGORY,
sum (case when
(CATEGORY = '1' and TIME >30) then sum (TIME - 30) or
(CATEGORY = '2' and TIME >90) then sum (TIME - 90) or
(CATEGORY = '3' and TIME >365) then sum (TIME - 365) else 0 end) as output
from database.table
group by CATEGORY
Thanks in advance!
I suspect the syntax you want is:
select CATEGORY,
sum(case when CATEGORY = '1' and TIME > 30 then TIME - 30
when CATEGORY = '2' and TIME > 90 then TIME - 90
when CATEGORY = '3' and TIME > 365 then TIME - 365
else 0
end) as output
from database.table
group by CATEGORY

Best way to find number of account within certain value ranges

I am having trouble what would be the best/most efficient way to find all accounts that fall within a certain value range without having 20 different selects.
So for example I need to find the following:
Account Value Number of plans average value
$100-$10000
$11000-$15000
$16000-$20000
$21000-$30000
$30000+
So right now my query is basically this for every value range:
SELECT COUNT (acct) AS 'Number of Plans'
, AVG (Value) AS 'Average Value'
FROM #RT1
WHERE Value
BETWEEN 0 AND 249999.99
AND InstaCode = 'S'
and there are three different charts that needs to be populated in SSRS. The only way I can figure it out is writing 15 different select statements but i feel there should be an easier and more effective way to do this.
Thanks!
I like to use cross apply for this:
SELECT v.grp, COUNT(acct) AS num_plans, AVG(value) as avg_value
FROM #RT1 t CROSS APPLY
(VALUES (CASE WHEN value >= 100 and value < 10000 THEN '$100-$10000'
WHEN value < 15000 THEN '$11000-$15000'
WHEN value < 20000 THEN '$16000-$20000'
WHEN value < 30000 THEN '$21000-$30000'
ELSE '$30000+'
END) as grp
) v(grp)
GROUP BY v.grp;
I'm not sure what InstaCode = 'S' has to do with the results. It is easy enough to add, either to the CASE expression or to a WHERE clause.
Use conditional aggregation for each group:
SELECT COUNT (case when Value
BETWEEN 0 AND 249999.99
then value else null end) AS 'Number of Plans group 1',
COUNT (case when Value
BETWEEN 2500000 AND 3000000
then value else null end) AS 'Number of Plans group 2',
AVG (case when Value
BETWEEN 0 AND 249999.99
then value else null end) AS 'Average Value 1st group', AVG (case when Value
BETWEEN 2500000 AND 3000000
then value else null end) AS 'Average Value 2nd group'...
from #RT1
where instacode='s'
SELECT
Case
When Value between 100 and 10000 Then '100 to 10000'
When Value between 11000 and 15000 Then '11000 to 15000'
When Value between 16000 and 20000 Then '16000 to 20000'
When Value between 21000 and 30000 Then '21000 to 30000'
When Value > 30000 Then '30000+'
End as AccountValue
COUNT (acct) AS NumberofPlans
, AVG (Value) AS AverageValue
FROM #RT1
WHERE Value
BETWEEN 0 AND 249999.99
AND InstaCode = 'S'
Group by
Case
When Value between 100 and 10000 Then '100 to 10000'
When Value between 11000 and 15000 Then '11000 to 15000'
When Value between 16000 and 20000 Then '16000 to 20000'
When Value between 21000 and 30000 Then '21000 to 30000'
When Value > 30000 Then '30000+'
End

Getting derived column from database | SQL query | DB2 SystemDate

My table is PRODUCTINFO:
Column name : product_name | launch_date
Sample Date : product1 2017-01-20
I need a SQL query to decide if the product is newly launched or not.
Business rule :
if (launch_date - currentdate < 0 && currentdate - launch_date < 90)
newly_launched = 'YES'
else
newly_launched = 'NO'
where currentdate is today's date.
SQL query I am witting is like :
SELECT launch_date, X as newly_launched
FROM PRODUCTINFO
WHERE product_name = 'product1'
I am not able to figure out correct replacement of 'X' in my query for desired result.
Problem I am facing is using currentdate and if else block in my query
Please help.
One way to get currentdate in DB2 using following query :
SELECT VARCHAR_FORMAT(CURRENT TIMESTAMP, 'YYYYMMDD')
FROM SYSIBM.SYSDUMMY1
Still not sure how to use this with my if else scenario.
Answer here don't solve my problem as this scenario is if-else based.
use TIMESTAMPDIFF with 16 as first parameter for count number days between 2 timestamps, for more detail look here
try this
select launch_date,
case when TIMESTAMPDIFF( 16, cast(cast(launch_date as timestamp) - current timestamp as char(22))) >0 and
TIMESTAMPDIFF( 16, cast(cast(launch_date as timestamp) - current timestamp as char(22))) <90 then 'YES' else 'NO' end newly
from PRODUCTINFO
WHERE product_name = 'product1'
simply use datediff function in a case syntax, I hope it could help!
SELECT launch_date,
case
when DATEDIFF(day,launch_date,getdate()) BETWEEN 0 AND 90 then 'YES'
else 'NO'
end AS newly_launched
FROM PRODUCTINFO
WHERE product_name = 'product1'
in case you want to do it for all your records:
SELECT launch_date,
case
when DATEDIFF(day,launch_date,getdate()) BETWEEN 0 AND 90 then 'YES'
else 'NO'
end AS newly_launched
FROM PRODUCTINFO

SQL Query to sum buckets

I have a table with inovices and a field named Arrers days and I need to count how many items are into 0-30 days also 30-90 and 90-120
Basically what I need is to calcuate the aging of my client portfolio.
so far I have this:
SELECT SUM(CASE WHEN Receivable.Arrers>'0'<'30' THEN 1 ELSE 0) AS 0-30,
SUM(CASE WHEN Receivable.Arrers<'30'>'60' THEN 1 ELSE 0) AS 30-60,
SUM(CASE WHEN Receivable.Arrers<'90'>'120' THEN 1 ELSE 0) AS 90-120
From Receivable
Table Name Receivable
Invoice Arrers
89859 10
89856 3
89853 11
Access SQL does not support CASE WHEN. You can use an IIf expression instead.
SELECT
SUM(IIf(r.Arrers BETWEEN 0 AND 30, 1, 0)) AS [0-30],
SUM(IIf(r.Arrers BETWEEN 31 AND 60, 1, 0)) AS [31-60],
SUM(IIf(r.Arrers BETWEEN 90 AND 120, 1, 0)) AS [90-120]
FROM Receivable AS r;
The example in your question ignores Arrers from 61 to 89, so I did, too. But you can add in another column expression if needed.
If Arrers is text instead of numeric datatype, you can use Val() to cast the text values to numbers ...
SUM(IIf(Val(r.Arrers) BETWEEN 0 AND 30, 1, 0)) AS [0-30]
You can try that one
SELECT
SUM( CASE WHEN Receivable.Arrers BETWEEN 0 AND 30 THEN 1 ELSE 0) AS '0-30',
SUM (CASE WHEN Receivable.Arrers BETWEEN 30 AND 60 THEN 1 ELSE 0) AS '30-60',
SUM (CASE WHEN Receivable.Arrers BETWEEN 90 AND 120 THEN 1 ELSE 0) AS '90-120',
arrers
FROM Receivable
GROUP BY arrers
1 thing need to be checked.
I've put BETWEEN 90 AND 120 as I consider it integer. If they are varchar, you will need to turn them into integers. Val(Receivable.Arrers) will do the trick on access. Please let me know if there is anything unclear
You are on the right track except possibly for the CASE stmt syntax .... try Andy's answer without the Group by
(Dont have enough points to post as a comment)
I'd use something like this:
SELECT subq.ArrersGroup, COUNT(*) 'Count'
FROM
(
SELECT
CASE
WHEN R.Arrers < 30 THEN '0-30'
WHEN R.Arrers < 90 THEN '30-90'
WHEN R.Arrers < 120 THEN '90-120'
ELSE 'Other'
END ArrersGroup
FROM Receivable R
) subq
GROUP BY subq.ArrersGroup
SqlFiddle

Calculate average for specific values

I'd like to calculate the average of a column but I'm getting stuck because, if a value is within a certain range than only I'd like to include that.
So if I had the following values:
100, 150, 500, 450, 300, 750
Now, I want to include only the values which is greater than 400 as a part of average.
then the query would calculate the average of these values:
500, 450, 750
and the output will be (500+450+750)/3
I tried
1)
select avg(case when value > 400 then value else 0 end) avg_val
from test
This is giving output as (0+0+500+450+0+750)/6. This is what not I wanted!
2)
select SUM(case when value > 400 then value else 0 end) /
SUM(case when value > 400 then 1 else 0 end) avg_val
from test
this is throwing error saying divide by zero if there is no value greater than 400.
Can anybody help ? I am using PostgreSQL.
Thanks!
Well, AVG() doesn't calculate null values, so you can use your query and replace 0 with NULL:
select avg(case when value > 400 then value else null end) avg_val
from test
Which can be formatted without the else part as well, since the default of the else is NULL
select avg(case when value > 400 then value end) avg_val
from test
Simply:
select avg(value)
from test
where value > 400
SELECT AVG(Price) AS PriceAverage FROM Products where price between 10 and 30;
SELECT AVG(Price) AS PriceAverage FROM bidding where price >=2500 ;