How to write case when statement to see if sum of 2 values equal? - sql

I am still learning sql so I apologize if this is easy stuff.
I need the sql code to sum the sales amount and sum the returnamount, and if they equal I want to mark that accountnumber with a 1 to know that it is fully cancelled.
i.[status], i.accountnumber,
case when sum(i.saleamount) = sum(r.returnamount) then 1 else 0 end as full_return_flag
from [idtable] i
join [returntable] r on r.id = i.id
> Account Number Sale Amount Return Amount Full_return_flag Status
> 1 500 250 1 Open
> 2 500 1500 1 Open
> 3 2000 0 0 Neutral
> 4 100 0 0 Closed

You want aggregation with case expression :
select i.status, i.accountnumber, sum(i.saleamount) as saleamount, sum(r.returnamount) as returnamount,
(case when sum(i.saleamount - r.returnamount) = 0 then 1 else 0 end) as Full_return_flag
from idtable] i inner join
returntable r
on r.id = t.id
group by i.status, i.accountnumber;

You need a full query:
select i.accountnumber,
sum(i.saleamount) as sale_amount, sum(r.returnamount) as return_amount,
(case when sum(i.saleamount) = sum(r.returnamount) then 1 else 0 end) as full_return_flag
from [idtable] i left join
[returntable] r
on r.id = i.id
group by i.accountnumber;
status is not in the result set so I don't think it is needed in the query. But you can include it both in the select and group by if you need it.

Related

Sum a column and perform more calculations on the result? [duplicate]

This question already has an answer here:
How to use an Alias in a Calculation for Another Field
(1 answer)
Closed 3 years ago.
In my query below I am counting occurrences in a table based on the Status column. I also want to perform calculations based on the counts I am returning. For example, let's say I want to add 100 to the Snoozed value... how do I do this? Below is what I thought would do it:
SELECT
pu.ID Id, pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed,
Snoozed + 100 AS Test
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
I get this error:
Invalid column name 'Snoozed'.
How can I take the value of the previous SUM statement, add 100 to it, and return it as another column? What I was aiming for is an additional column labeled Test that has the Snooze count + 100.
You can't use one column to create another column in the same way that you are attempting. You have 2 options:
Do the full calculation (as #forpas has mentioned in the comments above)
Use a temp table or table variable to store the data, this way you can get the first 5 columns, and then you can add the last column or you can select from the temp table and do the last column calculations from there.
You can not use an alias as a column reference in the same query. The correct script is:
SELECT
pu.ID Id, pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END)+100 AS Snoozed
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
MSSQL does not allow you to reference fields (or aliases) in the SELECT statement from within the same SELECT statement.
To work around this:
Use a CTE. Define the columns you want to select from in the CTE, and then select from them outside the CTE.
;WITH OurCte AS (
SELECT
5 + 5 - 3 AS OurInitialValue
)
SELECT
OurInitialValue / 2 AS OurFinalValue
FROM OurCte
Use a temp table. This is very similar in functionality to using a CTE, however, it does have different performance implications.
SELECT
5 + 5 - 3 AS OurInitialValue
INTO #OurTempTable
SELECT
OurInitialValue / 2 AS OurFinalValue
FROM #OurTempTable
Use a subquery. This tends to be more difficult to read than the above. I'm not certain what the advantage is to this - maybe someone in the comments can enlighten me.
SELECT
5 + 5 - 3 AS OurInitialValue
FROM (
SELECT
OurInitialValue / 2 AS OurFinalValue
) OurSubquery
Embed your calculations. opinion warning This is really sloppy, and not a great approach as you end up having to duplicate code, and can easily throw columns out-of-sync if you update the calculation in one location and not the other.
SELECT
5 + 5 - 3 AS OurInitialValue
, (5 + 5 - 3) / 2 AS OurFinalValue
You can't use a column alias in the same select. The column alias do not precedence / sequence; they are all created after the eval of the select result, just before group by and order by.
You must repeat code :
SELECT
pu.ID Id,pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END)+ 100 AS Test
FROM
Prospects p
INNER JOIN
ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE
p.Store = '108'
GROUP BY
pu.Name, pu.Id
ORDER BY
Name
If you don't want to repeat the code, use a subquery
SELECT
ID, Name, LeadCount, Working, Uninterested,Converted, Snoozed, Snoozed +100 AS test
FROM
(SELECT
pu.ID Id,pu.Name Name,
COUNT(*) LeadCount,
SUM(CASE WHEN Status = 'Working' THEN 1 ELSE 0 END) AS Working,
SUM(CASE WHEN Status = 'Uninterested' THEN 1 ELSE 0 END) AS Uninterested,
SUM(CASE WHEN Status = 'Converted' THEN 1 ELSE 0 END) AS Converted,
SUM(CASE WHEN SnoozedId > 0 THEN 1 ELSE 0 END) AS Snoozed
FROM Prospects p
INNER JOIN ProspectsUsers pu on p.OwnerId = pu.SalesForceId
WHERE p.Store = '108'
GROUP BY pu.Name, pu.Id) t
ORDER BY Name
or a view

Receive quantity of lines of query

Promt please.
I have an query: SELECT MAX(FPS.EndDateKey) WHERE FPS.EndDateKey <>-2
I need to receive quantity of lines of this query in other query:
SELECT INVS.ParticipantKey, MIN(CASE WHEN FPS.EndDateKey <> -2 AND FPS.EndDateKey > FPS.DefinedEndDateKey
AND FPS.EndDateKey > #PeriodEndDateKey AND FPS.DefinedEndDateKey < #PeriodEndDateKey
THEN 1
ELSE 0
END) as InRunout,
MAX(CASE WHEN DP.AccountKey = 6 THEN 1 ELSE 0 END) as HasHSA,
MAX(FPS.StartDateKey) as MostCurrentEnrollment,
CASE WHEN EXISTS(SELECT MAX(FPS.EndDateKey) WHERE FPS.EndDateKey <>-2) THEN 1 ELSE 0 END AS CurrentPlanYear
FROM SupportFile.InvoicableSubscription as INVS
INNER JOIN Evolution1.FactProductSubscription as FPS ON FPS.SubscriptionKey = INVS.SubscriptionKey AND FPS.StartDateKey = INVS.StartDateKey
INNER JOIN Evolution1.DimProduct as DP ON DP.ProductKey = FPS.ProductKey
GROUP BY INVS.ParticipantKey, FPS.EndDateKey
How I am able to do it? Prompt please. Thanks
You can achieve this via a subquery:
Psuedo code:
Select Name, description,
(select max(field) from table2) as maxtable2
from table 1

Using Math to calculate a percentage

OK, imagine I have a single column with 200 rows, in each row is a value either 4,8,2 or 0. Is there a way to divide the COUNT of times 2 and 0 occur against the COUNT of times 4 and 8 occur? Like this:
(COUNT(2, 0) / COUNT(4, 8)) * 100
Thanks! :)
EDIT:
Got a Divide By 0 error on the following statement, although doing COUNT(*) where Completion_Event_Type_Key IN (2,0) OR (8,4) returns over 3000000 rows
SELECT DISTINCT
SD.Last_Secondary_School_Name AS 'School', ((SELECT COUNT(*) where CC.Completion_Event_Type_Key IN (2,0))/(SELECT COUNT(*) where CC.Completion_Event_Type_Key IN (4, 8)))*100
FROM [AUTDataWarehouse].[dbo].[Fact_Admission] AS FA
INNER JOIN [AUTDataWarehouse].[dbo].[Dim_Student_Demographics] AS SD ON SD.Student_Demographics_Key = FA.Student_Demographics_Key
INNER JOIN [AUTDataWarehouse].[dbo].[Fact_SDR_Course_Completion] AS CC ON FA.Student_Demographics_Key = CC.Student_Demographics_Key
GROUP BY
SD.Last_Secondary_School_Name, CC.Completion_Event_Type_Key
ORDER BY SD.Last_Secondary_School_Name
You can do it like this:
select
sum(case when value in (2,0) then 1 else 0 end) /
sum(case when value in (4,8) then 1 else 0 end) * 100
from table
Beware division by zero, though. You might need special handling if there could be no rows with 4,8.
Given the query in your question, your SQL should look something like this:
SELECT
SD.Last_Secondary_School_Name AS 'School',
sum(case when CC.Completion_Event_Type_Key IN (2,0) then 1 else 0 end) /
sum(case when CC.Completion_Event_Type_Key IN (4,8) then 1 else 0 end) * 100
FROM [AUTDataWarehouse].[dbo].[Fact_Admission] AS FA
INNER JOIN [AUTDataWarehouse].[dbo].[Dim_Student_Demographics] AS SD ON SD.Student_Demographics_Key = FA.Student_Demographics_Key
INNER JOIN [AUTDataWarehouse].[dbo].[Fact_SDR_Course_Completion] AS CC ON FA.Student_Demographics_Key = CC.Student_Demographics_Key
GROUP BY SD.Last_Secondary_School_Name, CC.Completion_Event_Type_Key
ORDER BY SD.Last_Secondary_School_Name

SQL Count with multiple conditions then join

Quick one,
I have a table, with the following structure
id lid taken
1 1 0
1 1 0
1 1 1
1 1 1
1 2 1
Pretty simply so far right?
I need to query the taken/available from the lid of 1, which should return
taken available
2 2
I know I can simply do two counts and join them, but is there a more proficient way of doing this rather than two separate queries?
I was looking at the following type of format, but I can not for the life of me get it executed in SQL...
SELECT
COUNT(case taken=1) AS taken,
COUNT(case taken=0) AS available FROM table
WHERE
lid=1
Thank you SO much.
You can do this:
SELECT taken, COUNT(*) AS count
FROM table
WHERE lid = 1
GROUP BY taken
This will return two rows:
taken count
0 2
1 2
Each count corresponds to how many times that particular taken value was seen.
Your query is correct just needs juggling a bit:
SELECT
SUM(case taken WHEN 1 THEN 1 ELSE 0 END) AS taken,
SUM(case taken WHEN 1 THEN 0 ELSE 1 END) AS available FROM table
WHERE
lid=1
Alternatively you could do:
SELECT
SUM(taken) AS taken,
COUNT(id) - SUM(taken) AS available
FROM table
WHERE
lid=1
SELECT
SUM(case WHEN taken=1 THEN 1 ELSE 0 END) AS taken,
SUM(case WHEN taken=0 THEN 1 ELSE 0 END) AS available
FROM table
WHERE lid=1
Weird application of CTE's:
WITH lid AS (
SELECT DISTINCT lid FROM taken
)
, tak AS (
SELECT lid,taken , COUNT(*) AS cnt
FROM taken t0
GROUP BY lid,taken
)
SELECT l.lid
, COALESCE(a0.cnt, 0) AS available
, COALESCE(a1.cnt, 0) AS taken
FROM lid l
LEFT JOIN tak a0 ON a0.lid=l.lid AND a0.taken = 0
LEFT JOIN tak a1 ON a1.lid=l.lid AND a1.taken = 1
WHERE l.lid=1
;

SQL query with 2 grouping

This is a query that is executed without problem with MySql 5.0.51:
SELECT cc_split.idSplit,
count( voteup.vote),
count( votedown.vote)
FROM cc_split
LEFT JOIN cc_split_usager AS voteup ON voteup.idSplit = cc_split.idSplit
AND voteup.vote >0
LEFT JOIN cc_split_usager AS votedown ON votedown.idSplit = cc_split.idSplit
AND votedown.vote <0
WHERE cc_split.isArchived = false
GROUP BY cc_split.idSplit
LIMIT 0, 30
The problem is with the COUNT that doesn't display what I would like to have. Has you can see, it takes the cc_ split_ usager and should count the number of vote that is positive and on the other hand count the amount of vote that is negative. The code above display the number 2 upvote and 2 downvote when in fact it should be 2 upvote and 1 downvote. What is the trick to have in a single SQL query the number of upvote and downvote.
The tables look like:
cc_split:
-idSplit
-...
cc_split_usager:
-idSplit
-vote (can be +1 or -1)
-...
Any hint for me?
Try:
SELECT s.idSplit,
count(case when v.vote > 0 then 1 else null end) as VoteUp,
count(case when v.vote < 0 then 1 else null end) as VoteDown
FROM cc_split s
LEFT JOIN cc_split_usager AS v ON v.idSplit = s.idSplit
WHERE s.isArchived = false
GROUP BY s.idSplit
LIMIT 0, 30
try this:
SELECT s.idSplit,
Sum( Case When vote > 0 Then 1 Else 0 End) UpVotes,
Sum( Case When vote < 0 Then 1 Else 0 End) DownVotes
FROM cc_split s
LEFT JOIN cc_split_usager v
ON v.idSplit = s.idSplit
WHERE s.isArchived = false
GROUP BY s.idSplit
LIMIT 0 , 30