Case function with an alias - sql

I wish to use CASE statement with an alias that resulted of a quotient of two columns.
The code that I wrote is the follow, but it returns an error.
Could anyone help me with this?
SELECT TOP 5(ROUND(Registered_Students/Total_Student * 100,2)) AS Porcentaje, C.Total_Student, C.Registered_Students, S.Subject_Name, DATEPART(Year, C.Date) AS Año,
CASE WHEN Porcentaje >= 75 THEN 'Elected'
WHEN Porcentaje >= 50 THEN 'Elected 1'
ELSE 'Not elected' AS Elections
FROM Cohort AS C
INNER JOIN Subject AS S
ON S.Id_Subject = C.Id_Subject
ORDER BY Porcentaje

the syntax of SQL server case statement is not correct:
SELECT column1,
column2,
CASE WHEN CONDITION THEN 'Value1'
ELSE 'Value2' END AS columnX
FROM table
you just need to add END in your syntax and it will work fine

you have to insert the word; 'END' end of the line
SELECT C.Total_Student, C.Registered_Students, S.Subject_Name, DATEPART(Year, C.Date) AS Año,
CASE WHEN Porcentaje >= 75 THEN 'Elected'
WHEN Porcentaje >= 50 THEN 'Elected 1'
ELSE 'Not elected' END AS Elections
FROM Cohort AS C
INNER JOIN Subject AS S
ON S.Id_Subject = C.Id_Subject
ORDER BY Porcentaje
as-is
ELSE 'Not elected' AS Elections
to-be
ELSE 'Not elected' END AS Elections

A column alias can't be referenced in the same SELECT clause as it is created. Have a derived table (i.e. subquery in the FROM clause), where you create the Porcentaje column.
SELECT TOP 5 dt.*,
CASE WHEN Porcentaje >= 75 THEN 'Elected'
WHEN Porcentaje >= 50 THEN 'Elected 1'
ELSE 'Not elected'
END AS Elections
FROM
(
select
(ROUND(Registered_Students/Total_Student * 100,2)) AS Porcentaje,
C.Total_Student, C.Registered_Students, S.Subject_Name, DATEPART(Year, C.Date) AS Año
FROM Cohort AS C
INNER JOIN Subject AS S
ON S.Id_Subject = C.Id_Subject
) dt
ORDER BY Porcentaje

The standard case syntax is
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
WHEN conditionN THEN resultN
ELSE result
END;
You can fix your query by writing like this
Query will be easier to understand
Different indentation makes it easier to modify and to read
SELECT TOP 5
(ROUND(Registered_Students / Total_Student * 100, 2)) AS Porcentaje
, C.Total_Student
, C.Registered_Students
, S.Subject_Name
, DATEPART(YEAR, C.Date) AS Año,
(CASE
WHEN Porcentaje >= 75 THEN 'Elected'
WHEN Porcentaje >= 50 THEN 'Elected 1'
ELSE 'Not elected' END) AS Elections
FROM Cohort AS C
INNER JOIN Subject AS S
ON S.Id_Subject = C.Id_Subject
ORDER BY Porcentaje

Related

Oracle task issue in having clause

In the Having clause I am trying to compare 2 column because the condition is that CUST_CREDIT_LIMIT need to be multiply by 500 and compare with sum(s.amount_sold). Any ideas how to deal with this problem.
select (c.CUST_FIRST_NAME ||' '||c.CUST_LAST_NAME) as "CUSTOMER_NAME",
to_number(substr(c.CUST_INCOME_LEVEL, -7, 8),'999999999') as "UPPER_INCOM_LEVEL",
sum(s.amount_sold) as "TOTAL_AMOUNT",
(case when c.CUST_CREDIT_LIMIT <=1500 then 'Low limit'
when c.CUST_CREDIT_LIMIT >1500 then 'High limit'
end) as "CREDIT_LIMIT_LEVEL",
c.CUST_VALID
from sh.CUSTOMERS c JOIN sh.SALES s
on s.CUST_ID = c.CUST_ID
where CUST_VALID = 'A'
and CUST_INCOME_LEVEL like '%-%'
GROUP BY c.CUST_FIRST_NAME, c.CUST_LAST_NAME, s.AMOUNT_SOLD, c.CUST_INCOME_LEVEL, c.CUST_CREDIT_LIMIT, c.CUST_VALID
having s.amount_sold > c.CUST_CREDIT_LIMIT*500
It looks like you want to put the aggregation into a sub-query with just the SALES table and then JOIN the result of that to the CUSTOMERS table and can compare the credit limit to the sales amount in the join condition (and remove the GROUP BY and HAVING clauses from the outer query):
SELECT c.cust_first_name || ' ' || c.cust_last_name AS customer_name,
TO_NUMBER(SUBSTR(c.cust_income_level, -7), '9999999') AS upper_income_level,
s.total_amount,
CASE
WHEN c.cust_credit_limit <= 1500
THEN 'Low Limit'
WHEN c.cust_credit_limit > 1500
THEN 'High Limit'
END AS credit_limit_level,
c.cust_valid
FROM sh.customers c
INNER JOIN (
SELECT cust_id,
SUM(amount_sold) AS total_amount
FROM sh.sales
GROUP BY cust_id
) s
ON ( c.cust_id = s.cust_id
AND s.total_amount > c.cust_credit_limit * 500 )
WHERE c.cust_valid = 'A'
AND c.cust_income_level LIKE '%-%' -- Check for a hyphen anywhere
-- AND SUBSTR(c.cust_income_level, -8, 1) = '-' -- Check for a hyphen in a specific place
Just like you said (looks like you're missing the sum aggregate function in your having clause) (also, you should put all non-aggregated columns into the group by clause; yours contains s.amount_sold, while it shouldn't):
group by c.cust_first_name,
c.cust_last_name,
substr(c.cust_income_level, -7, 8),
case when c.cust_credit_limit <= 1500 then 'Low limit'
when c.cust_credit_limit > 1500 then 'High limit'
end,
c.cust_valid
having sum(s.amount_sold) > c.cust_credit_limit * 500

How to find the average of a subset of values from a table in PostgreSQL?

Forgive me but I am new to PostgreSQL and have been tasked with updating some fields in some tables. One particular field is the average decision time shown below:
CASE WHEN COUNT(tdrm.dbid) > 0
THEN TO_CHAR((AVG(tdrm.total_processing_time) || ' millisecond')::interval, 'MI:SS.MS')
ELSE '00:00.000'
END AS average_decision_time
Where COUNT(tdrm.dbid) is items_seen. The issue with this logic is that we want to exclude the total processing time from the average for items that have an abort flag equal to 'AF_ABORT'.
This is what I am trying to do:
CASE WHEN COUNT(tdrm.dbid) > 0
THEN TO_CHAR((AVG(COUNT(CASE WHEN tdrm.tdr_abort_flag!=AF_ABORT THEN tdrm.total_processing_time END)) || ' millisecond')::interval, 'MI:SS.MS')
ELSE '00:00.000'
END AS average_decision_time
But I am getting the error below :
ERROR: aggregate function calls cannot be nested
LINE 64: THEN TO_CHAR((AVG(COUNT(CASE WHEN tdrm.tdr_abort_flag!=A...
Am I on the right track or is there a simpler way of doing this?
Full SQL below:
SELECT s.*,
CASE WHEN agent_event.event_code = 'data_download' THEN 'DL'
WHEN agent_event.event_code = 'mode' THEN 'Mode'
ELSE agent_event.event_code
END AS userAction
FROM
(
WITH report_constants AS (
-- Decisions from DetectionReport.h
SELECT
0::int as AD_UNKNOWN,
1::int as AD_ALARM,
2::int as AD_CLEAR,
-- Flags from DetectionReport.h
0::int as AF_UNKNOWN,
1::int as AF_ABORT,
2::int as AF_SUCCESS,
-- UI values for Decisions are DIFFERENT
0::int as UI_AD_ALL,
1::int as UI_AD_CLEAR,
2::int as UI_AD_ALARM,
3::int as UI_AD_UNKNOWN,
--
0::int as AGENT_TYPE_SCANNER,
1::int as AGENT_TYPE_OSR,
2::int as AGENT_TYPE_DIVERTER,
3::int as AGENT_TYPE_TIP,
4::int as AGENT_TYPE_SEARCH,
-- Operation Mode from Module.h
0::int as OPERATION_MODE_UNKNOWN,
1::int as OPERATION_MODE_SCAN,
2::int as OPERATION_MODE_OTHER
)
SELECT
nss_user.username AS user_name,
reg_login.action_time AS login_action_time,
reg_logout.action_time AS logout_action_time,
to_char(reg_login.action_time, 'MM-DD-YYYY') AS login_date,
to_char(reg_login.action_time, 'HH24:MI:SS') AS login_time,
CASE WHEN reg_logout.action_time IS NULL THEN '' ELSE
to_char(reg_logout.action_time, 'MM-DD-YYYY') END AS logout_date,
CASE WHEN reg_logout.action_time IS NULL THEN '' ELSE
to_char(reg_logout.action_time, 'HH24:MI:SS') END AS logout_time,
CASE WHEN user_level.name LIKE 'Level %' THEN SUBSTRING(user_level.name from 7) ELSE user_level.name END AS userAccess,
COUNT(tdrm.dbid) AS items_seen,
CASE WHEN COUNT(tdrm.dbid) > 0
THEN ROUND(100.0 * COUNT(CASE WHEN tdrm.tdr_abort_flag=AF_SUCCESS
AND tdrm.tdr_alarm_decision=AD_CLEAR THEN 1 END) / COUNT(tdrm.dbid), 2)
ELSE 0.00
END AS clear_rate,
COUNT(CASE WHEN (tdrm.tdr_abort_flag=AF_SUCCESS
AND tdrm.tdr_alarm_decision=AD_UNKNOWN)
OR tdrm.tdr_abort_flag=AF_ABORT THEN 1 END) AS operator_timeout,
CASE WHEN COUNT(tdrm.dbid) > 0
THEN ROUND(100.0 * COUNT(CASE WHEN tdrm.tdr_abort_flag=AF_SUCCESS
AND tdrm.tdr_alarm_decision=AD_ALARM THEN 1 END) / COUNT(tdrm.dbid), 2)
ELSE 0.00
END AS suspect_rate,
CASE WHEN COUNT(tdrm.dbid) > 0
THEN ROUND(100.0 * COUNT(CASE WHEN
(tdrm.tdr_abort_flag=AF_SUCCESS AND tdrm.tdr_alarm_decision=AD_UNKNOWN)
OR tdrm.tdr_abort_flag=AF_ABORT THEN 1 END) / COUNT(tdrm.dbid), 2)
ELSE 0.00
END AS operatorNoDecisionRate,
CASE WHEN COUNT(tdrm.dbid) > 0
THEN TO_CHAR((AVG(CASE WHEN tdrm.tdr_abort_flag!=AF_ABORT THEN (tdrm.total_processing_time) END) || ' millisecond')::interval, 'MI:SS.MS')
ELSE '00:00.000'
END AS average_decision_time
v2_module.dbid AS v2_gem_dbid
FROM report_constants CROSS JOIN auth_event
INNER JOIN registration_event AS reg_login
ON reg_login.credential_id=auth_event.credential_id
AND reg_login.event_type=3
LEFT OUTER JOIN registration_event AS reg_logout
ON reg_logout.credential_id=auth_event.credential_id
AND reg_logout.event_type=4
INNER JOIN nss_user ON nss_user.dbid=auth_event.nss_user_dbid
INNER JOIN user_level ON user_level.dbid=nss_user.user_level_dbid
LEFT OUTER JOIN bag_tdr ON nss_user.dbid=bag_tdr.author_user_dbid
AND (item_tdr.agent_type=AGENT_TYPE_OSR OR
item_tdr.agent_type=AGENT_TYPE_SEARCH)
AND item_tdr.author_credential_id=auth_event.credential_id
LEFT OUTER JOIN v2_module AS tdrm ON
item_tdr.v2_module_dbid=tdrm.dbid
LEFT OUTER JOIN v2_general_equipment_module
ON v2_general_equipment_module.dbid=reg_login.v2_gem_dbid
WHERE auth_event.credential_id IS NOT NULL
AND auth_event.auth_event_type=1
AND ($P{userid} = 'ALL' OR $P{userid} = nss_user.username)
AND item_tdr.created_date >= $P{fromdate}
AND item_tdr.created_date <= $P{todate}
AND v2_module.operation_mode != OPERATION_MODE_OTHER
GROUP BY nss_user.username, user_level.name, reg_login.agent_type,
reg_login.action_time, reg_logout.action_time,
v2_module.dbid
) s
LEFT OUTER JOIN agent_event
ON s.v2_dbid=agent_event.v2_dbid
AND agent_event.event_timestamp >= s.login_action_time
AND (s.logout_action_time IS NULL OR agent_event.event_timestamp <= s.logout_action_time)
ORDER BY s.login_action_time
we want to exclude the total processing time from the average for items that have an abort flag equal to 'AF_ABORT'.
CASE WHEN count(tdrm.dbid) > 0
THEN to_char(avg(tdrm.total_processing_time)
FILTER (WHERE tdrm.tdr_abort_flag IS DISTINCT FROM 'AF_ABORT') -- ①, ②
* interval '1 millisecond' -- ③
, 'MI:SS.MS')
ELSE '00:00.000'
END AS average_decision_time
① Key element to implement your filter is the aggregate FILTER clause. See:
Conditional SQL count
② If tdrm.tdr_abort_flag can be NULL (missing info), we need tdrm.tdr_abort_flag IS DISTINCT FROM 'AF_ABORT'. Else we can simplify to tdrm.tdr_abort_flag <> 'AF_ABORT'.
The manual about "Comparison Predicates"
③ Multiplication is substantially faster than concatenation plus cast.
But after adding a FILTER like this, the expression can produce NULL values after all. Your requirements are fuzzy. You may really want:
Average of total_processing_time where tdr_abort_flag <> 'AF_ABORT'. Default to 0 if result is NULL for any reason:
COALESCE(to_char(avg(tdrm.total_processing_time) FILTER (WHERE tdrm.tdr_abort_flag <> 'AF_ABORT')
* interval '1 millisecond'
, 'MI:SS.MS')
, '00:00.000') AS average_decision_time
Or:
Average of total_processing_time where tdr_abort_flag <> 'AF_ABORT'. But only if count(tdrm.dbid) > 0. Default to 0 if result is NULL for any reason:
CASE WHEN count(tdrm.dbid) > 0
THEN COALESCE(to_char(avg(tdrm.total_processing_time) FILTER (WHERE tdrm.tdr_abort_flag <> 'AF_ABORT')
* interval '1 millisecond'
, 'MI:SS.MS')
, '00:00.000')
ELSE '00:00.000'
END AS average_decision_time
Or:
Average of total_processing_time where tdr_abort_flag <> 'AF_ABORT'. But only if count(tdrm.dbid) > 0 where tdr_abort_flag <> 'AF_ABORT'. Else default to 0
CASE WHEN count(tdrm.dbid) FILTER (WHERE tdrm.tdr_abort_flag <> 'AF_ABORT') > 0
THEN to_char(avg(tdrm.total_processing_time) FILTER (WHERE tdrm.tdr_abort_flag <> 'AF_ABORT')
* interval '1 millisecond'
, 'MI:SS.MS')
ELSE '00:00.000'
END AS average_decision_time
You mentioned that you are "new to SQL". Let me add this: a crystal-clear definition of the problem is > 50 % of the solution. True in many areas, but certainly with SQL.
You should be able to achieve this with a simple subquery that reselects from the table and filters out the aborted records with a WHERE clause when calculating the average. Something like this:
CASE WHEN COUNT(tdrm.dbid) > 0
THEN TO_CHAR((SELECT (AVG(CASE WHEN tdrm_subq.tdr_abort_flag != AF_ABORT
THEN (tdrm_subq.total_processing_time)
END) || ' millisecond')
FROM v2_module tdrm_subq, report_constants
WHERE tdr_abort_flag != AF_ABORT)::interval,
'MI:SS.MS')
ELSE '00:00.000'
END AS average_decision_time
Demo here: https://rextester.com/NSQN12214
Beside of subqueries, should mention that it is the exact case Window Functions intended for.

Trouble with SQL request

I just want to output only those records, that for the same name customer_name have cust_valid= 'I' and cust_valid='A'
I tried to do this, but the rezult for cust_valid have only records 'A' enter code here
SELECT c.cust_first_name ||' '|| c.cust_last_name AS CUSTOMER_NAME,
to_number(SUBSTR(c.cust_income_level, INSTR(c.cust_income_level, '-')+2), '999999') as UPPER_INCOME_LEVEL,
sum(s.amount_sold) as TOTAL_AMOUNT,
(CASE WHEN c.cust_credit_limit <= 1500 THEN 'Low_limit'
ELSE 'High_limit'
END) credit_limit_level,
c.cust_valid
FROM SH.customers c
JOIN sh.sales s on c.cust_id = s.cust_id
WHERE c.cust_valid = 'A' AND c.cust_income_level like '%-%'
GROUP BY c.cust_first_name, c.cust_last_name, c.cust_income_level, c.cust_credit_limit, c.cust_valid
HAVING SUM(s.amount_sold) > (c.cust_credit_limit * 50)
ORDER BY UPPER_INCOME_LEVEL DESC, CUSTOMER_NAME;
You need to use the EXISTS clause in WHERE condition as follows:
and exists (select 1
from sh.customers cin
where cin.cust_id = c.cust_id
and c.c.cust_valid = 'I'
)
If I followed you correctly, you can implement this filtering by modifying your having clause:
HAVING
SUM(s.amount_sold) > (c.cust_credit_limit * 50)
AND MAX(CASE WHEN cust_valid= 'I' THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN cust_valid= 'A' THEN 1 ELSE 0 END) = 1
and cust_valid='A'
I tried, but no result.
I dont think I showed the condition correct.
I have to explain...
Modify the query to display the name of the client (CUSTOMER_NAME)
for which there are rows with CUST_VALID = ‘A’ and rows with
CUST_VALID = ‘I’ as a separate result.

Using CASE instead of using UNION in ORACLE SQL

I have a query like this shown below
SELECT A.BUSINESS_UNIT
,A.ASSET_ID
,A.BOOK
,A.FISCAL_YEAR
,A.ACCOUNTING_PERIOD
,A.CATEGORY
,A.ACCOUNT_FA AS ACCOUNT
,A.CURRENCY_CD
,'COST_ADD' AS MOT
,A.COST_ADD AS AMOUNT
FROM SYSADM.PS_DEPR_RPT A
WHERE A.BOOK IN ('CORP-BASE','CORPORATE')
AND NOT (A.COST_ADD=0)
UNION
SELECT A1.BUSINESS_UNIT
,A1.ASSET_ID
,A1.BOOK
,A1.FISCAL_YEAR
,A1.ACCOUNTING_PERIOD
,A1.CATEGORY
,A1.ACCOUNT_AD AS ACCOUNT
,A1.CURRENCY_CD
,'ACCUM_DEPR_ADD' AS MOT
,A1.ACCUM_DEPR_ADD AS AMOUNT
FROM SYSADM.PS_DEPR_RPT A1
WHERE A1.BOOK IN ('CORP-BASE','CORPORATE')
AND NOT (A1.ACCUM_DEPR_ADD=0);
I want to remove the UNION which I have used in this to increase the performance of the query. As it is taking around 120sec to run the query, I want to make it more efficient. I used CASE for this. Query run time is less(4sec) but, i have a data mismatch here. Please help me on this.
PS: Query that I prepared using CASE is given below. Kindly suggest if any corrections required.
select BUSINESS_UNIT
,ASSET_ID
,BOOK
,FISCAL_YEAR
,ACCOUNTING_PERIOD
,CATEGORY
,ACCOUNT
,MOT
,AMOUNT
FROM
(SELECT A.BUSINESS_UNIT
,A.ASSET_ID
,A.BOOK
,A.FISCAL_YEAR
,A.ACCOUNTING_PERIOD
,A.CATEGORY
,A.ACCOUNT_FA AS ACCOUNT
,A.CURRENCY_CD,
case when NOT (A.COST_ADD=0) then 'COST_ADD'
when NOT (A.ACCUM_DEPR_ADD=0) then 'ACCUM_DEPR_ADD'
else 'x'
END
as MOT,
case when NOT (A.COST_ADD=0) then A.COST_ADD
when NOT (A.ACCUM_DEPR_ADD=0) then A.ACCUM_DEPR_ADD
else 0
END
as AMOUNT
FROM SYSADM.PS_DEPR_RPT A
WHERE A.BOOK IN ('CORP-BASE','CORPORATE')) X
where mot <> 'x' and amount <> 0;
You can try this.
SELECT
A.BUSINESS_UNIT
,A.ASSET_ID
,A.BOOK
,A.FISCAL_YEAR
,A.ACCOUNTING_PERIOD
,A.CATEGORY
,A.ACCOUNT_FA AS ACCOUNT
,A.CURRENCY_CD
,CASE WHEN A.COST_ADD <> 0 THEN 'COST_ADD' ELSE 'ACCUM_DEPR_ADD' END AS MOT
,CASE WHEN A.COST_ADD <> 0 THEN A.COST_ADD ELSE A.ACCUM_DEPR_ADD END AS AMOUNT
FROM SYSADM.PS_DEPR_RPT A
WHERE A.BOOK IN ('CORP-BASE','CORPORATE');
SELECT
A.BUSINESS_UNIT
,A.ASSET_ID
,A.BOOK
,A.FISCAL_YEAR
,A.ACCOUNTING_PERIOD
,A.CATEGORY
,A.ACCOUNT_FA AS ACCOUNT
,A.CURRENCY_CD
,CASE WHEN A.COST_ADD <> 0 THEN 'COST_ADD' ELSE 'ACCUM_DEPR_ADD' END AS MOT
,CASE WHEN A.COST_ADD <> 0 THEN A.COST_ADD ELSE A.ACCUM_DEPR_ADD END AS AMOUNT
FROM SYSADM.PS_DEPR_RPT A
WHERE A.BOOK IN ('CORP-BASE','CORPORATE')
and (A.COST_ADD <> 0 or A.ACCUM_DEPR_ADD <> 0);

SQL Query using calculation in SELECT not working

the purpose of this query is to take the percentage of late orders to total number of orders. So the calculation part of this query is only pulling back zeros. Everything else works. I commented the part below that is pulling back zeros. What am I doing wrong?
SELECT
case LM.DState
WHEN 'OH' THEN 'OH Order'
WHEN 'NC' THEN 'NC Order'
WHEN 'TX' THEN 'TX Order'
WHEN 'WA' THEN 'WA order'
Else 'Other'
End as 'OrderType',
count(LM.OrderID) as 'Vol',
sum(case
WHEN datediff(day, S.Appt, S.Arrived) > 0 THEN 1
else 0
END) as 'TotalLates',
--PART THAT IS ONLY PULLING ZEROS, SHOULD BE NON-ZERO
CAST (
sum(case WHEN datediff(day, S.Appt, S.Arrived) > 0 THEN 1 else 0 END)
/ count(LM.OrderID)
as decimal(5,2)
) *100 as 'OTD%'
FROM ((Customers.dbo.CusOrders as LM WITH (NOLOCK) inner join
Customers.dbo.CusLocations as S WITH (NOLOCK) on LM.OrderID = S.OrderID) inner join
Customers.dbo.Loads as L with (NOLOCK) on LM.OrderID = L.OrderID)
WHERE LM.CusCode = 'Domestic'
GROUP BY case LM.DState
WHEN 'OH' THEN 'OH Order'
WHEN 'NC' THEN 'NC Order'
WHEN 'TX' THEN 'TX Order'
WHEN 'WA' THEN 'WA Order
Else 'Other'
End
It's due to rounding that occurs with integer division, since you're calculating a percentage the value is always below 1, and that gets rounded to 0, you can fix this by multiplying the numerator by 1.0:
CAST (
sum(case WHEN datediff(day, S.Appt, S.Arrived) > 0 THEN 1 else 0 END)*1.0
/ count(LM.OrderID)
as decimal(5,2)
) *100 as 'OTD%'
For example:
SELECT 5/7
--0
SELECT 5.0/7
--0.714285