Access alias in CASE statement - sql

I am trying to create a column called DateStartedStatus that utilizes a previously aliased column to compute its value. It should use CurrentStatus to output a value and an error is showing that says "Invalid column name 'CurrentStatus'". How can I access that alias in the below case statement?
SELECT p.[ID]
,p.[Name] as 'ProcurementName'
,p.[FundingDocumentNumber] as 'FundingDocumentNumber'
,p.[Status]
,p.[Comments] as 'Comments'
,p.[isSAVE]
,p.[InWorkDate]
,p.[RoutedDate]
,p.[FundsCertifiedDate]
,p.[AwardedDate]
,p.[TransactionType]
,p.[FNMSStatus]
,p.[Closed]
,p.[Archived]
,p.[Cancelled]
,(CASE
WHEN p.[Status] = 'In Work' THEN 'Pending'
ELSE p.[Status]
END) as CurrentStatus
,(CASE
WHEN CurrentStatus = 'Awarded' THEN p.AwardedDate <-- fails here CurrentStatus not a column
END) as DateStartedStatus
,(SELECT SUM(TotalCost)
FROM ProcurementsRequestLineItems subprlis
LEFT JOIN RequestLineItems subrli ON subprlis.RequestLineItemID = subrli.ID
WHERE ProcurementID = p.ID) as TotalCost
FROM Procurements p
WHERE p.Closed = 0 AND p.Archived = 0;

Use a subquery as suggested by leftjoin, or move the CurrentStatus logic to a CTE. I prefer CTE as they are more legible to me, but I know many prefer a subquery as it is right in the middle of the code, and in a longer query or one with many CTE's that can be a more legible route.
WITH CurrentStatus
AS
(
SELECT
... -- at least one JOIN'able column back to the main query
,(CASE
WHEN p.[Status] = 'In Work' THEN 'Pending'
ELSE p.[Status]
END) as CurrentStatus
FROM ...
)

Using subqueries like this
select ... CASE WHEN CurrentStatus ....
from
( --calculate Current_status here
select ....
CASE
WHEN p.[Status] = 'In Work' THEN 'Pending'
ELSE p.[Status]
END) as CurrentStatus
...
) s
Do not worry, subquery will not add computational complexity, optimizer will remove it if possible.
Another way is nested CASE expressions (query is not readable):
case when case ... some logic here ... end = 'Awarded'
then ...
end

For SQL Server, I would use a CROSS APPLY instead of an subquery, because I prefer it for readability. For one-condition evaluation, I use IIF instead of CASE.
SELECT p.[ID], p.[Name] AS [ProcurementName], p.[FundingDocumentNumber] AS [FundingDocumentNumber],
p.[Status], p.[Comments] AS [Comments], p.[isSAVE], p.[InWorkDate], p.[RoutedDate], p.[FundsCertifiedDate],
p.[AwardedDate], p.[TransactionType], p.[FNMSStatus], p.Closed, p.[Archived], p.[Cancelled],
cur.CurrentStatus, start.DateStartedStatus, tot.TotalCost
FROM Procurements AS p
CROSS APPLY (SELECT IIF(p.[Status] = 'In Work', 'Pending', p.[Status]) AS CurrentStatus) AS cur
CROSS APPLY (SELECT IIF(cur.CurrentStatus = 'Awarded', p.AwardedDate, null) AS DateStartedStatus) AS start
CROSS APPLY (
SELECT SUM(TotalCost) AS name
FROM ProcurementsRequestLineItems AS subprlis
LEFT JOIN RequestLineItems AS subrli ON subprlis.RequestLineItemID = subrli.ID
WHERE ProcurementID = p.ID
) AS tot
WHERE p.Closed = 0 AND p.Archived = 0;
I would also avoid using the reserved word "Status" as a column identifier.

Related

Arithmetic overflow error converting varchar to data type numeric CASE statement

I am trying to write a query that returns an "Estimated Annual Value", and for this, I am using a Case statement. There are two inner joins involved in the query.
So, the query and gives me result when I am running this piece:
select Users.Id, Name, PhoneNumber, Email, Currency, count(*) as TotalOrders, sum(TotalCustomerAmount) as TotalOrderValue, avg(TotalCustomerAmount) as AverageOrderValue, TsCreate as RegistrationDate, max(TsOrderPlaced) as LastOrderDate, min(TsOrderPlaced) as FirstOrderDate,
CASE
When PromotionsEnabled = 0 then 'Y'
When PromotionsEnabled = 1 then 'n'
else 'undefined'
end as Promotions,
/*CASE
When ((DATEDIFF(day, max(TsOrderPlaced), min(TsOrderPlaced)) >= 6) AND (count(*) > 3)) then ((sum(TotalCustomerAmount)/(DATEDIFF(day, max(TsOrderPlaced), min(TsOrderPlaced))))*365)
Else '-'
end as EstimatedAnnualValue*/
from AspNetUsers with (nolock)
inner join Orders with (nolock) on Orders.UserId = AspNetUsers.Id and Orders.WhiteLabelConfigId = #WhiteLabelConfigId
and Orders.OrderState in (2,3,4,12)
inner join UserWhiteLabelConfigs with (nolock) on UserWhiteLabelConfigs.UserId = AspNetUsers.Id and Orders.WhiteLabelConfigId = #WhiteLabelConfigId
where AspNetUsers.Discriminator = 'ApplicationUser'
group by Users.Id, Name, Number, Currency, Email, TsCreate, PromotionsEnabled
But the problem comes when I am running this with the second CSAE statement, is it because I cannot use the aggregate function before GROUP BY? I am also thinking of using a Subquery
Looking fr some help!!
You need to use aggregation functions. For instance, if you want 'Y' only when all values are 0 or NULL:
(case when max(PromotionsEnabled) = 0 then 'Y'
when max(PromotionsEnabled) = 1 then 'N'
else 'undefined'
end) as Promotions,
I'm not sure if this is the logic you want (because that detail is not in the question). However, this shows that you can use aggregation functions in a case expression.

Is it possible to have multiple CASE in a GROUP BY that can make it so it doesn't group at all?

I'm trying to query come datas and on a spcific case I might have to group datas by multiple values. But most of the time it needs not to be grouped at all. So I'm using multiple CASE WHEN {...} inside the GROUP BY, and all the WHEN basically have the same condition. The problem is that if the condition is met, everything works fine. But if it's false, then the GROUP BY section is empty and the query returns only the first row.
I basically tried to reorganize the quesry in every way that came to my mind, nothing seemed to work, and I didn't find anything conclusive on internet.
I'm using MySql 5.7.
SELECT
{element I want to select}
FROM
{tables}
WHERE
{conditions}
GROUP BY
CASE WHEN (condition) THEN [table].[column] END,
CASE WHEN (condition) THEN [table].[column] END,
CASE WHEN (condition) THEN [table].[column] END
ORDER BY
{...}
Full query :
SELECT
tx.code,
IFNULL(hr.label,'') AS rh_label,
IFNULL(cli.label,'') AS client_label,
DATE(FROM_UNIXTIME(created.value / 1000)) AS Created,
IFNULL(item_enfant.label,'') As Parasite,
IFNULL(item_parent.label,'') As Zone,
CASE
WHEN :perWeek = 'week' THEN SUM(qte.value)
ELSE qte.value
END AS Quantite,
CEILING(DATEDIFF(DATE(FROM_UNIXTIME(created.value / 1000)), DATE(FROM_UNIXTIME(:from / 1000))) / 7) AS Weeks
FROM tx
LEFT JOIN tx_type AS tt ON tt.id = tx.tx_type_id
LEFT JOIN human_resource AS hr ON hr.id = tx.human_resource_id
LEFT JOIN client AS cli ON cli.id = tx.client_id
LEFT JOIN tx_state AS ts ON ts.id = tx.current_tx_state_id
LEFT JOIN workflow_step AS ws ON ws.id = ts.workflow_step_id
LEFT JOIN item AS item_enfant ON item_enfant.item_list_id = tx.item_list_id
JOIN item_type AS ite ON ite.id = item_enfant.item_type_id
LEFT JOIN item_meta AS qte ON qte.item_id = item_enfant.id AND qte.name = 'qtePourRapport'
LEFT JOIN item_prop AS created ON created.item_id = item_enfant.id AND created.name = 'visite.timestamp'
JOIN item AS item_parent ON item_parent.id = item_enfant.parent_item_id
JOIN item_type AS itp ON itp.id = item_parent.item_type_id
WHERE
ite.name = 'parasite' AND
item_enfant.product_id IN (:parasiteIds) AND
itp.name = 'zone' AND
item_parent.product_id IN (:zoneIds) AND
cli.id = (:clientId) AND
ws.logic_id = 600 AND
created.value BETWEEN :from AND :to AND
created.value IS NOT NULL AND qte.value IS NOT NULL
GROUP BY
CASE WHEN :perWeek = 'week' THEN item_enfant.label END, #Parasite
CASE WHEN :perWeek = 'week' THEN item_parent.label END, #Zone
CASE WHEN :perWeek = 'week' THEN CEILING(DATEDIFF(DATE(FROM_UNIXTIME(created.value / 1000)), DATE(FROM_UNIXTIME(:from / 1000))) / 7) END #Weeks
ORDER BY
Created;
I'm getting the datas of the first row alone. And I actually have no idea how to get it just not to group if the condition is not met.
You need a unique value for the aggregation or two separate queries. The simplest method might be union all:
select . . .
from t
where <conditions not to group by>
union all
select . . .
from t
where <conditions to group by>
group by . . .;
You need to be sure that each subquery returns compatible columns.
SELECT
{element I want to select}
FROM
{tables}
WHERE
{conditions}
GROUP BY
CASE WHEN (condition) THEN [table].[column] ELSE [some unique value of same data-type as column] END,
CASE WHEN (condition) THEN [table].[column] ELSE [some unique value of same data-type as column] END,
CASE WHEN (condition) THEN [table].[column] ELSE [some unique value of same data-type as column] END
ORDER BY
{...}
I guess the missing ELSE clause will evaluate to NULL. This is constant, thus all rows will be in the same group, thus there will be only one row returned for this group. To avoid grouping you need unique values over all returned rows in the combination of the grouping-elements (not in every single grouping-element as stated erlier).
EDIT
Thus the soultion from the comment might be easier: Just add another grouping-element CASE WHEN !(condition) THEN CONCAT([different elements making it unique]) END

SQL Server CASE statement with subquery in then

I have the case statement below but despite it having the values that should trigger the case's, I am still getting nulls. Any obvious errors?
SELECT
*,
(CASE
WHEN topicname = 'TPOSTAL_CODE'
THEN (SELECT LTRIM(RTRIM(ALS_C)) AS LocationCode
FROM ODS.TBUS_LOC
WHERE cci.columna = ods.TBUS_LOC.BUS_LOC_I)
WHEN topicname = 'TBUSINESS_SITE'
THEN (SELECT LTRIM(RTRIM(B.ALS_C)) AS LocationCode
FROM ODS.TBUSINESS_SITE A
INNER JOIN ODS.TBUS_LOC B ON A.BUS_LOC_ID = B.BUS_LOC_I
WHERE cci.columna = A.BUS_LOC_ID)
END) AS Code
FROM
[XREF].[tablea] CCI
CCIEntityName with a distinct
It could be that topicname contains spaces either at the beginning or the end. These may not be readily visible to the eye. You might try
CASE WHEN ltrim(rtrim(topicname)) = 'TPOSTAL_CODE'
and see if that makes a difference.

How to use a non-existing column in sql query

I am working in SQL server 2012. I have to write a sql statement where I first assign a value to [Pay_Type], which is a non-existing column (not sure whether it can be called as variable or not) and based upon its value I want to use it in another case statement as shown below
SELECT sp.First_Name, [Pay_Type] = CASE WHEN NOT EXISTS(SELECT '1' FROM
PERSON_SALARY ps WHERE ps.PARTY_ID = sp.PARTY_ID and ps.END_DATE IS NULL)
THEN 'Hourly' ELSE 'Salary' END,
HOURLY_RATE = CASE WHEN [Pay_Type] = 'Hourly' THEN pj.HOURLY_RATE ELSE
'0.00' END
FROM SEC_PERSON sp
LEFT OUTER JOIN PERSON_JOB pj ON sp.PERSON_ID = pj.PERSON_ID
WHERE sp.END_DATE IS NOT NULL
But I am getting "Invalid column name 'Pay_Type' " error.
Column aliases cannot be re-used in the same SELECT where they are define. The typical answer is to use a subquery or CTE. I also like using a lateral join:
SELECT sp.First_Name, s.Pay_Type,
HOURLY_RATE = (CASE WHEN s.Pay_Type = 'Hourly' THEN pj.HOURLY_RATE ELSE
'0.00' END)
FROM SEC_PERSON sp LEFT OUTER JOIN
PERSON_JOB pj
ON sp.PERSON_ID = pj.PERSON_ID OUTER APPLY
(SELECT (CASE WHEN NOT EXISTS (SELECT 1
FROM PERSON_SALARY ps
WHERE ps.PARTY_ID = sp.PARTY_ID and ps.END_DATE IS NULL
)
THEN 'Hourly' ELSE 'Salary'
END) as PayType
) s
WHERE sp.END_DATE IS NOT NULL

ORA-00934: group function is not allowed here when adding case when

I have a piece of code which runs fine. However, when i am introducing a "case when" statement in the select clause, I get the "group function is not allowed here" error and I cannot fix it (the issue relates to the last Group by function in my code)
Any idea why (don't be put off by the code, it is 3 joins together, apparently the issue is caused by the last Group By statement) ?
Thank you!
SELECT
Trans_Table.MTAGRE01_NO
, (case when Cash. MTAGRE01_NO = Trans_Table. MTAGRE01_NO
then (SUM(Trans_Table.MTTRANS01_VALUENCU)*-1)
else SUM(Trans_Table.MTTRANS01_VALUENCU) END) AS MTTRANS01_VALUENCU
FROM MTTRANS01 Trans_Table
INNER JOIN RUTRANTYPE01 Trans_Type
ON Trans_Type.RUTRANTYPE01_CODE = Trans_Table.RUTRANTYPE01_CODE
LEFT JOIN(
SELECT
MTAGRE01_NO
,CASE WHEN SRAGRESTAT01_CODE = 'COLL' THEN MTAGRE01_AGRESTATDATE END AS Date_Fr
from MTAGRE01
where CASE WHEN SRAGRESTAT01_CODE = 'COLL' THEN MTAGRE01_AGRESTATDATE END is not null
) F_Date
ON F_Date.MTAGRE01_NO = Trans_Table.MTAGRE01_NO
LEFT JOIN(
SELECT
Trans_Table.MTAGRE01_NO
FROM MTTRANS01 Trans_Table
INNER JOIN RUTRANTYPE01 Trans_Type ON Trans_Type.RUTRANTYPE01_CODE = Trans_Table.RUTRANTYPE01_CODE
GROUP BY
Trans_Table.MTAGRE01_NO, Trans_Type.RUTRANTYPE01_CODE, Trans_Type.RUTRANTYPE01_DESCRIPTION
) Cash
ON Cash.MTAGRE01_NO = Trans_Table.MTAGRE01_NO
where Trans_Type.SRPROCTYPE01_CODE in ('C','D')
and Trans_Table.MTTRANS01_VALUEDATE >= F_Date.Date_Fr
GROUP BY
Trans_Table.MTAGRE01_NO
, (case when Cash. MTAGRE01_NO = Trans_Table. MTAGRE01_NO
then (SUM(Trans_Table.MTTRANS01_VALUENCU)*-1)
else SUM(Trans_Table.MTTRANS01_VALUENCU) END);
I believe it's hung up on the sum inside the case statement. 2 routes to correct that I can see, likely alot more:
This is a little hacky, but it'll work and give results fast. Change your case:
SELECT
Trans_Table.MTAGRE01_NO
, (case when Cash. MTAGRE01_NO = Trans_Table. MTAGRE01_NO
then ((Trans_Table.MTTRANS01_VALUENCU)*-1)
else (Trans_Table.MTTRANS01_VALUENCU) END) AS MTTRANS01_VALUENCU
Remove the case from the group by.
Now call your entire query a sub query
Select MTAGRE01_NO, sum(MTTRANS01_VALUENCU)
(your entire query)a
You other option that takes a bit more work...in your initial from statement:
MTTRANS01 Trans_Table
Change that to a subquery that joins to the trans table and returns
case when Cash. MTAGRE01_NO = Trans_Table. MTAGRE01_NO
then ((Trans_Table.MTTRANS01_VALUENCU)*-1)
else (Trans_Table.MTTRANS01_VALUENCU) END as MTAGRE01_NO
Then join to that subquery and do a simple sum in your main query.
Hopefully that all makes sense, ask questions if you need clarifications