Oracle task issue in having clause - sql

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

Related

SQL Query to use a Case Statement within and Aggregate Function

I have a query below that I need to get a count where Incident Records = 0. The first level is a summary of the records it grabs to calculate totals. I tried using COUNT(INCIDENTS!='0') but it apparently is not working though it does not error out. How would I get this count? This is in Snowflake if that is a factor. The case statement: CASE WHEN SUM(INCIDENTS) = '0' THEN '0' ELSE COUNT(INCIDENTS!='0') END AS INCIDENTS Works but the 2nd line which references those Incidents seems to only give a count but not the result of the Case Statement before it:
SELECT DRIVER_NAME, DRIVER_ID, CSC, SUM(DISTINCT(OBSERVATIONS)) AS OBSERVATIONS, --SUM(DISTINCT(INCIDENTS)) AS INCIDENTS_SUM_DISTINCT,
--COUNT(INCIDENTS) AS INCIDENTS,
COUNT(INCIDENTS!='0'),
CASE WHEN SUM(INCIDENTS) = '0' THEN '0' ELSE COUNT(INCIDENTS!='0') END AS INCIDENTS,
(COUNT(INCIDENTS!='0') / SUM(DISTINCT(OBSERVATIONS))) * 100 AS FREQUENCY_PERCENT,
1 AS GOAL, (SUM(SIX_TEN_MPH) / SUM(DISTINCT(OBSERVATIONS))) * 100 AS SIX_TEN_PERCENT,
(SUM(ELEVEN_FIFTEEN_MPH) / SUM(DISTINCT(OBSERVATIONS))) * 100 AS ELEVEN_FIFTEEN_PERCENT,
(SUM(SIXTEEN_PLUS_MPH) / SUM(DISTINCT(OBSERVATIONS))) * 100 AS SIXTEEN_PLUS_PERCENT
--SPEED_LIMIT, SPEED, DIFFERENCE, REPORT_DATE, TIME,
FROM
(
SELECT A.DRIVER_NAME AS DRIVER_NAME, A.DRIVER_ID AS DRIVER_ID, C.TRC_TERMINAL AS CSC, A.OBSERVATIONS AS OBSERVATIONS,
A.INCIDENTS AS INCIDENTS, B.SPEED_LIMIT AS SPEED_LIMIT, B.SPEED AS SPEED, B.DIFFERENCE AS DIFFERENCE,
A.REPORT_DATE AS REPORT_DATE, B.TIME AS TIME,
CASE WHEN DIFFERENCE >= 6 AND DIFFERENCE <= 10 THEN '1' WHEN DIFFERENCE IS NULL THEN '0' ELSE '0' END AS SIX_TEN_MPH,
CASE WHEN DIFFERENCE > 10 AND DIFFERENCE <= 15 THEN '1' WHEN DIFFERENCE IS NULL THEN '0' ELSE '0' END AS ELEVEN_FIFTEEN_MPH,
CASE WHEN DIFFERENCE > 15 THEN '1' WHEN DIFFERENCE IS NULL THEN '0' ELSE '0' END AS SIXTEEN_PLUS_MPH
FROM "PROD"."PUBLIC"."SG_DRIVER_TREND" A
LEFT JOIN "PROD"."PUBLIC"."SG_DRIVER_INCIDENTS" B
ON A.DRIVER_ID = B.DRIVER_ID
LEFT JOIN "PROD"."PUBLIC"."TMW_TRACTORPROFILE" C
ON B.Vehicle = C.TRC_NUMBER
WHERE A.DRIVER_ID != ''
AND A.REPORT_DATE BETWEEN '2022-07-01' AND '2022-07-31'
AND B.TIME BETWEEN '2022-07-01' AND '2022-07-31'
AND SUBSTRING(B.TIME, 0, 10) <= A.REPORT_DATE -- Less than or equal to report date
AND SUBSTRING(B.TIME, 0, 10) > SUBSTRING(DATEADD(week,-1, A.REPORT_DATE), 0, 10) -- Greater than 1 week ago
UNION
--SELECT A.DRIVER_NAME AS DRIVER_NAME, A.DRIVER_ID AS DRIVER_ID, C.TRC_TERMINAL AS CSC, A.OBSERVATIONS AS OBSERVATIONS,
-- A.INCIDENTS AS INCIDENTS, B.SPEED_LIMIT AS SPEED_LIMIT, B.SPEED AS SPEED, B.DIFFERENCE AS DIFFERENCE,
-- A.REPORT_DATE AS REPORT_DATE, B.TIME AS TIME,
SELECT DISTINCT(A.DRIVER_NAME) AS DRIVER_NAME, A.DRIVER_ID AS DRIVER_ID,
C.TRC_TERMINAL AS CSC,
--'UPT' AS CSC,
A.OBSERVATIONS AS OBSERVATIONS,
'0' AS INCIDENTS, '0' AS SPEED_LIMIT, '0' AS SPEED, '0' AS DIFFERENCE,
A.REPORT_DATE AS REPORT_DATE, '' AS TIME,
'0' AS SIX_TEN_MPH,
'0' AS ELEVEN_FIFTEEN_MPH,
'0' AS SIXTEEN_PLUS_MPH
FROM "PROD"."PUBLIC"."SG_DRIVER_TREND" A
LEFT JOIN "PROD"."PUBLIC"."SG_DRIVER_INCIDENTS" B
ON A.DRIVER_ID = B.DRIVER_ID
-- AND SUBSTRING(B.TIME, 0, 10) <= A.REPORT_DATE -- Less than or equal to report date
-- AND SUBSTRING(B.TIME, 0, 10) > SUBSTRING(DATEADD(week,-1, A.REPORT_DATE), 0, 10) -- Greater than 1 week ago
INNER JOIN "PROD"."PUBLIC"."TMW_TRACTORPROFILE" C
ON B.Vehicle = C.TRC_NUMBER
WHERE NOT EXISTS (SELECT DRIVER_ID FROM "PROD"."PUBLIC"."SG_DRIVER_INCIDENTS" D
WHERE A.DRIVER_ID = D.DRIVER_ID
AND D.TIME BETWEEN '2022-07-01' AND '2022-07-31'
AND SUBSTRING(D.TIME, 0, 10) <= A.REPORT_DATE -- Less than or equal to report date
AND SUBSTRING(D.TIME, 0, 10) > SUBSTRING(DATEADD(week,-1, A.REPORT_DATE), 0, 10) -- Greater than 1 week ago
)
AND A.DRIVER_ID != '' AND A.INCIDENTS = '0'
AND A.REPORT_DATE BETWEEN '2022-07-01' AND '2022-07-31'
--AND B.DRIVER_ID IS NULL --<-- For LEFT JOIN
ORDER BY DRIVER_ID
)
--WHERE INCIDENTS != 0
GROUP BY DRIVER_ID, DRIVER_NAME, CSC
ORDER BY DRIVER_ID
This line:
CASE WHEN SUM(INCIDENTS) = '0' THEN '0' ELSE COUNT(INCIDENTS!='0') END AS INCIDENTS seems to give me the correct number of incidents but when I try and copy it into this line as the Incident Count: (COUNT(INCIDENTS!='0') / SUM(DISTINCT(OBSERVATIONS))) * 100 AS FREQUENCY_PERCENT
like: (COUNT(CASE WHEN SUM(INCIDENTS) = '0' THEN '0' ELSE COUNT(INCIDENTS!='0') END) / SUM(DISTINCT(OBSERVATIONS))) * 100 AS FREQUENCY_PERCENT, I get the following error:
SQL compilation error: Aggregate functions cannot be nested: [SUM(CAST("values".INCIDENTS AS FLOAT))] nested in [COUNT(IFF((SUM(CAST("values".INCIDENTS AS FLOAT))) = (CAST('0' AS FLOAT)), TO_NUMBER('0', 18, 0), COUNT(CAST("values".INCIDENTS != '0' AS BOOLEAN))))]
I have seen examples online of nested Aggregate Functions working. Code examples are appreciated.
The first line:
CASE WHEN SUM(INCIDENTS) = '0' THEN '0' ELSE COUNT(INCIDENTS!='0') END
is a conditional aggreation, simplified to COUNT_IF:
COUNT_IF(INCIDENTS != '0') AS INCIDENTS
The second one:
COUNT_IF(INCIDENTS != '0')/NULLIF(SUM(DISTINCT OBSERVATIONS)),0) * 100
AS FREQUENCY_PERCENT
It is always good to secure against division by 0, thus NULLIF(exp, 0).

Case function with an alias

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

Needing Help Separating Data into columns

I'm currently using SQL at work to Query a database in order to display certain information. I have each item that's being pulled as separate entities. Whenever I run the Query my results only show under one column. Is there a way to separate this data into separate columns based on alias?
SELECT
count(o.orderid) AS Current_Daily
FROM
orders o
WHERE
o.ship_dt BETWEEN '2020-11-09 00:00:00' AND '2020-11-15 23:59:59'
AND o.orderstatus = 2
UNION
#UNION ALL
SELECT
count(o.orderid) AS Previous_Daily
FROM
orders o
WHERE
o.ship_dt BETWEEN '2019-11-09 00:00:00' AND '2019-11-15 23:59:59'
AND o.orderstatus = 2
UNION
#UNION ALL
SELECT
count(o.orderid) AS Current_Monthly
FROM
orders o
WHERE
o.ship_dt BETWEEN '2020-11-01 00:00:00' AND '2020-11-15 23:59:59'
AND o.orderstatus = 2
UNION
#UNION ALL
SELECT
count(o.orderid) AS Previous_Monthly
FROM
orders o
WHERE
o.ship_dt BETWEEN '2019-11-01 00:00:00' AND '2019-11-15 23:59:59'
AND o.orderstatus = 2
;
Any Help would be greatly appreciated.
Use conditional aggregation, as in:
select
sum(case when ship_dt >= '2020-11-09' and ship_dt < '2020-11-15' then 1 else 0 end) as current_daily,
sum(case when ship_dt >= '2020-11-01' and ship_dt < '2020-11-15' then 1 else 0 end) as current_monthly,
... -- more conditional "sum()"s if needed
from orders o
where orderstatus = 2
You probably just want conditional aggregation. Something like this:
SELECT SUM(CASE WHEN o.ship_dt >= '2020-11-09' AND o.ship_dt < '2020-11-16' THEN 1 ELSE 0 END) AS Current_Daily,
SUM(CASE WHEN o.ship_dt >= '2020-11-01' AND o.ship_dt < '2020-11-16' THEN 1 ELSE 0 END) AS Current_Monthly,
FROM orders o
WHERE o.orderstatus = 2;
You can add additional columns using similar logic.

Oracle SQL: SUM of 'amount_sold' rows for each client

I need to display for each client the total /sum/ amount (from amount_sold) saved as a total_amount field. And in WHERE to set that total_amount is greater than or equal to cust_credit_limit :
total_amount >= cust_credit_limit
//
SELECT CONCAT (CONCAT(cust_first_name,' '),cust_last_name) AS customer_name,
amount_sold,
(CASE WHEN cust_credit_limit<=1500 THEN 'Low limit'
ELSE 'High limit'
END) AS credit_limit_level,
cust_valid
FROM sh.customers JOIN sh.sales
ON customers.cust_id = sales.cust_id
ORDER BY customer_name ASC;
The results now look like this:
But I need only one row for each client with the sum of all amount_sold for this client AS total_amount
**EDIT: I tried as recommended in comments and it worked. But I have another condition - to order the results by 'upper_income_level' and when I add
'lpad( substr(cust_income_level, instr( cust_income_level, '-') + 2 ), 9, '0') AS upper_income_level,'
it appears "not a GROUP BY expression".
'SELECT CONCAT (CONCAT(cust_first_name,' '),cust_last_name) AS customer_name,
lpad( substr(cust_income_level, instr( cust_income_level, '-') + 2 ), 9, '0') AS upper_income_level,
SUM(amount_sold) as total_sold,
(CASE WHEN cust_credit_limit<=1500 THEN 'Low limit'
ELSE 'High limit'
END) AS credit_limit_level,
cust_valid
FROM sh.customers JOIN sh.sales
ON customers.cust_id = sales.cust_id
WHERE cust_valid = 'A' AND cust_income_level LIKE '%-%'
GROUP BY
CONCAT (CONCAT(cust_first_name,' '),cust_last_name),
cust_valid,
cust_credit_limit
HAVING SUM(amount_sold) >= 50*cust_credit_limit
ORDER BY upper_income_level DESC, customer_name ASC;'
Your query shall deal with customers and their total sale. So, select from the customers table and join the aggregated total sale:
select
c.cust_first_name || ' ' || c.cust_last_name as customer_name,
to_number
(
regexp_substr(c.cust_income_level , '[0123456789,]+$'),
'999999999D999',
'nls_numeric_characters = '',.'''
) as upper_income_level,
s.total_sale,
case when c.cust_credit_limit <= 1500
then 'Low limit'
else 'High limit'
end as credit_limit_level,
c.cust_valid
from sh.customers c
join
(
select cust_id, sum(amount_sold) as total_sale
from sh.sales
group by cust_id
) s on s.cust_id = c.cust_id
and s.total_sale >= c.cust_credit_limit
where c.cust_valid = 'A'
and c.cust_income_level like '%-%'
order by upper_income_level desc, customer_name;
Add a SUM and a GROUP BY:
SELECT CONCAT (CONCAT(cust_first_name,' '),cust_last_name) AS customer_name,
SUM(amount_sold) as total_sold,
(CASE WHEN cust_credit_limit<=1500 THEN 'Low limit'
ELSE 'High limit'
END) AS credit_limit_level,
cust_valid
FROM sh.customers JOIN sh.sales
ON customers.cust_id = sales.cust_id
GROUP BY
CONCAT (CONCAT(cust_first_name,' '),cust_last_name),
cust_valid,
cust_credit_limit
HAVING SUM(amount_sold) >= cust_credit_limit
ORDER BY customer_name ASC;
Tips:
You might find that your concat accepts multiple parameters and concats all of them e.g. CONCAT(first_name, ' ', last_name)
Rules of GROUP BY: Anything not contained in a SUM, AVG, or similar aggregation function in your SELECT, must be in the GROUP BY. At the time GROUP BY is done, the aliases in the select list don't exist, so you must instead use the expression that prepares the result (or some child part of it such that the whole expression can be computed from grouped values)
HAVING is like a where clause that is done after a group by. WHERE is done before a group by. HAVING can hence reference grouped and aggregated expressions, but they must be grouped or aggregated
Try to use sum(), group by and having:
SELECT CONCAT (CONCAT(cust_first_name,' '),cust_last_name) AS customer_name,
sum(amount_sold) as total_amount,
(CASE WHEN cust_credit_limit<=1500 THEN 'Low limit'
ELSE 'High limit'
END) AS credit_limit_level,
cust_valid
FROM sh.customers JOIN sh.sales
ON customers.cust_id = sales.cust_id
GROUP BY customer_name, credit_limit_level, cust_valid
HAVING sum(amount_sold)>=max(cust_credit_level)
ORDER BY customer_name ASC;
if you have 2 cutomer with the same name,cust_id in group by is the must.
SELECT customers.cust_id,
CONCAT (CONCAT(cust_first_name,' '),cust_last_name) AS customer_name,
sum(isnull(amount_sold,0)) amount_sold
FROM sh.customers JOIN sh.sales
ON customers.cust_id = sales.cust_id
group by customers.cust_id,CONCAT (CONCAT(cust_first_name,' '),cust_last_name)
having sum(isnull(amount_sold,0)) >= max(cust_credit_limit)
ORDER BY customer_name ASC;

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.