Using result of a SQL case to compare and return another case - sql

I'm trying to compare the result of a sum which returns an amount, by using a case that would return the result of this comparison:
SELECT u.name, u.account,
sum (case u.account
when t.sent_to then t.quantity * -1
else t.quantity end) as quantity_sent,
case when quantity_sent > u.available then 'YES'
else 'NO' end as analysis_result
from users as u inner join trans as t
on u.account in (t.sent_to, t.received_by)
group by u.name, u.account;
Whenever I call quantity_sent back, I get an "Invalid column name quantity_sent". Is there any other way I can use the result on this newly created column to compare inside a case?

Try this-
SELECT
u.name, u.account,
SUM (
CASE u.account
WHEN t.sent_to THEN t.quantity * -1
ELSE t.quantity
END
) AS quantity_sent,
CASE
WHEN SUM (
CASE u.account
WHEN t.sent_to then t.quantity * -1
ELSE t.quantity
END ) > u.available THEN 'YES'
ELSE 'NO'
END as analysis_result
FROM users as u
INNER JOIN trans as t
ON u.account in (t.sent_to, t.received_by)
GROUP BY u.name, u.account;

You need to repeat the expression or use a subquery:
select name, account, quantity_sent,
(case when quantity_sent > u.available then 'YES'
else 'NO'
end) as analysis_result
from (select u.name, u.account,
sum(case u.account
when t.sent_to then t.quantity * -1
else t.quantity
end) as quantity_sent
from users u inner join
trans t
on u.account in (t.sent_to, t.received_by)
group by u.name, u.account
) u;
This is part of the general rule in SQL that you cannot re-use a column alias in the SELECT where it is defined. There is a simple reason for this: SQL does not guarantee the order of evaluation of the expressions in the SELECT.

Related

Sum values in two different tables and join results keeping the columns

I have two tables: one with downtime and the other with productive time.
I want to have a table like this
But I am getting this
In the result, I am getting twice the downtime of the sum for the report 04102021-1, but as can be seen in the second picture, the value is present only once.
The script I am using is the following:
SELECT WAJ.REPORTCODE,--BASIC_REPORT_TABLE.TECHNICIAN,BASIC_REPORT_TABLE.JOBREPORTCODE,
SUM(CASE WHEN DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED) /3600.0
END) AS PRODUCTION_TIME,
SUM(CASE WHEN DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED) /3600.0
END) AS DOWNTIME
FROM WORK_AT_JOB WAJ,WAITING_AT_SITE WAS
WHERE (WAJ.REPORTCODE=WAS.REPORTCODE AND WAJ.REPORTCODE LIKE '04102021%') GROUP BY WAJ.REPORTCODE
After the #xQbert comment, I tried this:
SELECT WAS.REPORTCODE,
SUM(CASE WHEN DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED) /3600.0
END) AS PRODUCTION_TIME,
SUM(CASE WHEN DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED) /3600.0
END) AS DOWNTIME
FROM WAITING_AT_SITE WAS
JOIN WORK_AT_JOB WAJ
ON (WAJ.REPORTCODE=WAS.REPORTCODE AND WAS.REPORTCODE LIKE '04102021%') GROUP BY WAS.REPORTCODE
But I got the same result.
May you give some advice to get the result I want?
Thanks in advance
You could use conditional aggregation for this, but the easiest, and probably most performant, way to do this is to pre-aggregate the results before you join.
SELECT
waj.REPORTCODE
waj.PRODUCTION_TIME,
was.DOWNTIME
FROM (
SELECT
waj.REPORTCODE,
SUM(CASE WHEN v.diff < 0 THEN 86400 + v.diff ELSE v.diff END / 3600.0) AS PRODUCTION_TIME
FROM WORK_AT_JOB waj
CROSS APPLY (VALUES( DATEDIFF(SECOND, waj.TIMESTARTED, waj.TIMEFINISHED) )) v(diff)
WHERE waj.REPORTCODE LIKE '04102021%'
GROUP BY
waj.REPORTCODE
) waj
JOIN (
SELECT
was.REPORTCODE,
SUM(CASE WHEN v.diff < 0 THEN 86400 + v.diff ELSE v.diff END / 3600.0) AS PRODUCTION_TIME
FROM WAITING_AT_SITE was
CROSS APPLY (VALUES( DATEDIFF(SECOND, was.TIMESTARTED, was.TIMEFINISHED) )) v(diff)
WHERE was.REPORTCODE LIKE '04102021%'
GROUP BY
was.REPORTCODE
) was ON waj.REPORTCODE = was.REPORTCODE;
Note the use of CROSS APPLY (VALUES to avoid code repetition.

HAVING gives me "column...does not exist" but I see the column

This is a practice question from stratascratch and I'm literally stuck at the final HAVING statement.
Problem statement:
Find the total number of downloads for paying and non-paying users by date. Include only records where non-paying customers have more downloads than paying customers. The output should be sorted by earliest date first and contain 3 columns date, non-paying downloads, paying downloads.
There are three tables:
ms_user_dimension (user_id, acc_id)
ms_acc_dimension (acc_id, paying_customer)
ms_download_facts (date, user_id, downloads)
This is my code so far
SELECT date,
SUM(CASE WHEN paying_customer = 'no' THEN cnt END) AS no,
SUM(CASE WHEN paying_customer = 'yes' THEN cnt END) AS yes
FROM (
SELECT date, paying_customer, SUM(downloads) AS cnt
FROM ms_download_facts d
LEFT JOIN ms_user_dimension u ON d.user_id = u.user_id
LEFT JOIN ms_acc_dimension a ON u.acc_id = a.acc_id
GROUP BY 1, 2
ORDER BY 1, 2
) prePivot
GROUP BY date
HAVING no > yes;
If I remove the HAVING no > yes at the end, the code will run and I can see I have three columns: date, yes, and no. However, if I add the HAVING statement, I get the error "column "no" does not exist...LINE 13: HAVING no > yes"
Can't figure out for the sake of my life what's going on here. Please let me know if anyone figures out something. TIA!
You don't need a subquery for this:
SELECT d.date,
SUM(CASE WHEN a.paying_customer = 'no' THEN d.downloads END) AS no,
SUM(CASE WHEN a.paying_customer = 'yes' THEN d.downloads END) AS yes
FROM ms_download_facts d LEFT JOIN
ms_user_dimension u
ON d.user_id = u.user_id LEFT JOIN
ms_acc_dimension a
ON u.acc_id = a.acc_id
GROUP BY d.date
HAVING SUM(CASE WHEN a.paying_customer = 'no' THEN d.downloads END) > SUM(CASE WHEN a.paying_customer = 'yes' THEN d.downloads END);
You can simplify the HAVING clause to:
HAVING SUM(CASE WHEN a.paying_customer = 'no' THEN 1 ELSE -1 END) > 0
This version assumes that paying_customer only takes on the values 'yes' and 'no'.
You may be able to simplify the query further, depending on the database you are using.
It doesn't like aliases in the having statement. Replace no with:
SUM(CASE WHEN paying_customer = 'no' THEN cnt END)
and do the similar thing for yes.
SELECT date,
SUM(CASE WHEN paying_customer = 'no' THEN cnt END) AS no,
SUM(CASE WHEN paying_customer = 'yes' THEN cnt END) AS yes
FROM (
SELECT date, paying_customer, SUM(downloads) AS cnt
FROM ms_download_facts d
LEFT JOIN ms_user_dimension u ON d.user_id = u.user_id
LEFT JOIN ms_acc_dimension a ON u.acc_id = a.acc_id
GROUP BY 1, 2
ORDER BY 1, 2
) prePivot
GROUP BY date
HAVING SUM(CASE WHEN paying_customer = 'no' THEN cnt END) > SUM(CASE WHEN paying_customer = 'yes' THEN cnt END);

Firebird SQL: query slow due to coalesce or can it be rewritten

i'm having some performance problems with a frequently used query.
SELECT
v.id,
coalesce((SELECT sum(amount) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1 AND atype_ref in (1,3,4)), 0) "fv",
coalesce((SELECT sum(amount) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1 AND atype_ref=2), 0) "ivo",
coalesce((SELECT sum(amount) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1 AND atype_ref=5), 0) "iio",
coalesce((SELECT sum(amount * mvalue) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1), 0) "vw"
FROM productvariant v
since artjournal is a big table and gets thousands of new records each day the performance is getting terrible.
I have indices on all ID fields.
Is there a way to rewrite this statement to speed things up? Or can i use a different way to retrieve the data from the artjournal table and return 0 if result is null?
Thanks for your thoughts,
Christiaan
Looks like you want a filtered aggregate:
SELECT v.id,
sum(case when a.atype_ref in (1,3,4) then a.amount else 0 end) as "fv",
sum(case when a.atype_ref = 2 then a.amount else 0 end) as "ivo",
sum(case when a.atype_ref = 5 then a.amount else 0 end) as "iio",
sum(a.amount * a.mvalue) as "vw"
FROM productvariant v
LEFT JOIN artjournal a ON a.variant_ref = v.id
WHERE storage_ref = 1
GROUP BY v.id;

Using CASE in SQL Server - getting "because it is not contained in either an aggregate function or the GROUP BY clause."

I want to show the days since the last customer order from a certain storenumber, I have been told to use CASE.
I don't want to use MAX or MIN because it may ignore other records for said customer.
SELECT ms.CustomerID AS email,
AS last_txn_days_online,
CASE
WHEN ST2.StoreNumber != '100799' THEN CEILING(Round(DateDiff(DAY, Min(st2.PurchaseDate), Max(st2.PurchaseDate)) / NULLIF(Count(st2.CustomerID) - 1, 0),0))
ELSE NULL
END AS last_txn_days_instore
FROM [Not MS] ms
LEFT JOIN [ORDER_HEADER] st2 ON ms.CustomerID = st2.CustomerID
GROUP BY MS.CustomerID
I think you want conditional aggregation. The query would look something like this:
SELECT ms.CustomerID AS email,
CEILING(Round(DateDiff(DAY,
Min(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.PurchaseDate END),
M MAX(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.PurchaseDate END)
) / NULLIF(Count(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.CustomerID END) - 1, 0), 0
)
)
END AS last_txn_days_instore
FROM [Not MS] ms LEFT JOIN
[ORDER_HEADER] st2
ON ms.CustomerID = st2.CustomerID
GROUP BY MS.CustomerID

Is it possible to use 'case' with and in 'count'?

Is it possible to use case with and in count
SELECT branches.NAME AS agence,
count(
CASE loanstatus
WHEN '1'
AND Datepart(month,loanaccount.issuedate)= 2 THEN 1
ELSE NULL
END )AS nombre_de_credits_demande ,
count(
CASE loanstatus
WHEN '2' datepart(month,loanaccount.chargeoffdate)= 2 THEN 1
ELSE NULL
END )AS nombre_de_credits_approuve
please help me
You can use it with count(). I prefer sum():
select Branches.Name as Agence,
sum(case when LoanStatus = '1' and
datepart(MONTH, LoanAccount.IssueDate) = 2
then 1 else 0
end ) as Nombre_de_Crédits_Demandé ,
sum(case when LoanStatus = '2' and
datepart(MONTH, LoanAccount.IssueDate) = 2
then 1 else 0
end ) as Nombre_de_Crédits_Approuvé
The issue with your code was not the count() versus sum() it is the mixing of two different case syntaxes. When you use case <var> when <val>, you cannot include any other conditions. Just use when with the full conditions that you want.
And, if you like, you can use count() instead of sum().
And, for conciseness, I prefer the month() function:
select Branches.Name as Agence,
sum(case when LoanStatus = '1' and MONTH(LoanAccount.IssueDate) = 2
then 1 else 0
end ) as Nombre_de_Crédits_Demandé ,
sum(case when LoanStatus = '2' and MONTH(LoanAccount.IssueDate) = 2
then 1 else 0
end ) as Nombre_de_Crédits_Approuvé
you can achieve your goal through this query.
select
branches.name as agence
,(select COUNT(1) from <table_name> where loginstatus=1 and Datepart(month,loanaccount.issuedate)= 2) as nombre_de_credits_demande
,(select COUNT(1) from <table_name> where loginstatus=2 and Datepart(month,loanaccount.issuedate)= 2) as AS nombre_de_credits_approuve
from <Table_name>