Troubleshooting Errors with Two SUMs - sql

I have a table, it's going to be used for a supplier scorecard, with eleven different fields that can be assigned a value of 1-5. Null values are allowed.
I need to write a query that will calculate the average of the fields that are filled out by each row. In other words, I might be dividing TOTAL by 11 in one row, and dividing TOTAL by 5 in another.
I'm working with this query:
select
cf$_vendor_no,
cf$_party,
cf$_environmental,
cf$_inspections,
cf$_invoice_process,
cf$_ncr,
cf$_on_time_delivery,
cf$_qms,
cf$_safety,
cf$_schedule,
cf$_scope_of_work,
cf$_turn_times,
sum(nvl(cf$_environmental,0)
+nvl(cf$_inspections,0)
+nvl(cf$_invoice_process,0)
+nvl(cf$_ncr,0)
+nvl(cf$_on_time_delivery,0)
+nvl(cf$_qms,0)
+nvl(cf$_safety,0)
+nvl(cf$_schedule,0)
+nvl(cf$_scope_of_work,0)
+nvl(cf$_turn_times,0))
/
sum(
case when cf$_environmental is not null then 1 else 0 end +
case when cf$_inspections is not null then 1 else 0 end +
case when cf$_invoice_process is not null then 1 else 0 end +
case when cf$_ncr is not null then 1 else 0 end +
case when cf$_on_time_delivery is not null then 1 else 0 end +
case when cf$_qms is not null then 1 else 0 end +
case when cf$_safety is not null then 1 else 0 end +
case when cf$_schedule is not null then 1 else 0 end +
case when cf$_scope_of_work is not null then 1 else 0 end +
case when cf$_turn_times is not null then 1 else 0 end) --as "average"
from supplier_scorecard_clv
group by cf$_vendor_no, cf$_party, cf$_environmental, cf$_inspections, cf$_invoice_process, cf$_ncr, cf$_on_time_delivery, cf$_qms, cf$_safety, cf$_schedule, cf$_scope_of_work, cf$_turn_times
And, it almost works.
The first SUM in my code will add the values in each row to give me a total. I get a total 25 for the first FARW002 row, I get 6 for the second, and 12 for the third.
The second SUM in my code works as well. I get a count of 6 for my first FARW002 row, 2 for my second, and 3 for my third.
However, when I try to combine these, like in the code snippet above, I get a "ORA-00923: FROM keyword not found where expected" error and I'm not sure why.

So, this is stupid but here's what the problem ended up being:
+nvl(cf$_turn_times,0))
/
sum(
When I changed the code to this - really I was just dicking around - it worked:
+nvl(cf$_turn_times,0))/sum(
So, something about having the / and SUM separated from the rest of the query - which I only do to make the code more readable for me - was causing the issue.
Thanks for nothing Juan!

Related

Oracle 11g Nested Case Statement Calculation

In Oracle 11g, I am trying to get to a sell price from a query of data. Yes I can export this and write the code somewhere else, but I want to try to do this elegantly in the query.
I only seem to get the first part of the equation and not the last CASE where I use:
WHEN sales_code
What I am ultimately trying to do is take the result from the top and divide it by the bottom except in the case of SALE_CODE 4 where I add 1+1 or 2 to the top result and then divide by the equation.
round(to_number(price) *
CASE WHEN class_code='X'
THEN .48
ELSE .5
END * e1.set_qty +
CASE WHEN carton_pack_qty = '1'
THEN 0
ELSE (
CASE WHEN NVL(SUBSTR(size, 1,NVL(LENGTH(size) - 2,0)),1) > '35'
THEN 3.5
ELSE 3
END)
END +
CASE
WHEN sales_code='1' THEN 0 /(1-17/100)
WHEN sales_code='2' THEN 0 /(1-5/100)
WHEN sales_code='3' THEN 0 /(1-18/100)
WHEN sales_code='4' THEN 1+1 / (1-9.5/100)
WHEN sales_code='5' THEN 0 /(1-17/100)
WHEN sales_code='6' THEN 0 /(1-8/100)
WHEN sales_code='7' THEN 0 /((1-150)/100)
ELSE (100/100)
END,2) AS "Price",
I get a result from the query, but not the whole calculation. I tried this many other ways and there was always an error with parentheses or some other arbitrary error.
Any help would be appreciated.
I think this is your problem:
WHEN sales_code='1' THEN 0 /(1-17/100)
CASE returns a scalar, a number. You're trying to have it return the second half of the formula in your calculation. You need something more like this:
...
END +
CASE WHEN sales_code='4' THEN 1 ELSE 0 END /
CASE
WHEN sales_code='1' THEN (1-17/100)
WHEN sales_code='2' THEN (1-5/100)
WHEN sales_code='3' THEN (1-18/100)
WHEN sales_code='4' THEN (1-9.5/100)
WHEN sales_code='5' THEN (1-17/100)
WHEN sales_code='6' THEN (1-8/100)
WHEN sales_code='7' THEN ((1-150)/100)
ELSE 1 END ...
Actually, I'm not entirely sure what you're trying to do with sales_code='4', but that looks close.
I think I understand now what you are trying to do. Almost at least :-)
The first thing you should do is write down the complete formula with parentheses where needed. Something like:
final = ((price * class_code_factor * set_qty) + quantity_summand + two_if_sales_code4) * sales_code_factor
(That last part looks like a percentage factor, not a divisor to me. I may be wrong of course.)
Once you have the formula right, translate this to SQL:
ROUND
(
(
(
TO_NUMBER(price) *
CASE WHEN class_code = 'X' THEN 0.48 ELSE 0.5 END *
e1.set_qty
)
+
CASE WHEN carton_pack_qty = 1 THEN 0
ELSE CASE WHEN NVL(SUBSTR(size, 1,NVL(LENGTH(size) - 2,0)),1) > '35'
THEN 3.5
ELSE 3
END
END
+
CASE WHEN sales_code = 4 THEN 2 ELSE 0 END
)
*
CASE
WHEN sales_code = 1 THEN 1 - (17 / 100)
WHEN sales_code = 2 THEN 1 - (5 / 100)
WHEN sales_code = 3 THEN 1 - (18 / 100)
WHEN sales_code = 4 THEN 1 - (9.5 / 100)
WHEN sales_code = 5 THEN 1 - (17 / 100)
WHEN sales_code = 6 THEN 1 - (8 / 100)
WHEN sales_code = 7 THEN (1 - 150) / 100)
ELSE 1
END
, 2 ) AS "Price",
Adjust this to the formula you actually want. There are some things I want to point out:
Why is price not a number in your database, but a string that you must convert to a number with TO_NUMBER? That must not be. Store values in the appropriate format in your database.
In a good database you would not have to get a substring of size. It seems you are storing two different things in this column, which violates database normalization. Separate the two things and store them in separate columns.
The substring thing looks strange at that, too. You are taking the left part of the size leaving out the last two characters. It seems hence that you don't know the lenth of the part you are getting, so let's say that this can be one, two or three characers. (I don't know of course.) Now you compare this result with another string; a string that contains a numeric value. But as you are comparing strings, '4' is greater than '35', because '4' > '3'. And '200' is lesser than '35' because '2' < '3'. Is this really intended?
There are more things you treated as strings and I took the liberty to change this to numbers. It seems for instance that a quantity (carton_pack_qty) should be stored as a number. So do this and don't compare it to the string '1', but to the number 1. The sales code seems to be numeric, too. Well, again, I may be wrong.
In a good database there would be no magic numbers in the query. Knowledge belongs in the database, not in the query. If a class code 'X' means a factor of 0.48 and other class codes mean a factor of 0.5, then why is there no table of class codes showing what a class code represents and what factor to apply? Same for the mysterious summand 3 resp. 3.5; there should be a table holding these values and the size and quantity ranges they apply to. And at last there is the sales code which should also be stored in a table showing the summand (2 for code 4, 0 elsewise) and the factor.
The query part would then look something like this:
ROUND((price * cc.factor * el.set_qty) + qs.value + sc.value) * sc.factor, 2) AS "Price"
Breaking the dividend into a sub query worked and then adding parentheses around it to divide by in the main query worked.
(
select
style,
to_number(price) *
CASE WHEN class_code='X'
THEN .48
ELSE .5
END * set_qty +
CASE WHEN carton_pack_qty = '1'
THEN 1
ELSE (
CASE WHEN to_number(NVL(SUBSTR(size, 1,NVL(LENGTH(size) - 2,0)),1)) > 35
THEN 3.5
ELSE 3
END)
END as Price
FROM STYL1 s1,STY2 s2
WHERE s1.style=s2.style
) P1

Sqlite Get counts of all distinct values across a row

For a personal end of the year project I've scraped my attendance off the school website hoping to do some form of visualization of the data. I've now gotten stuck transforming that data into the form I need it in.
Currently my database looks like this
Date,One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Dee
2014-09-03,P,P,P,P,AU,AU,P,T*,AU,P
2014-09-04,P,P,P,P,N/A,AU,P,T*,N/A,P
2014-09-05,P,P,P,P,AU,AU,P,P,P,P
2014-09-09,P,P,P,P,AU,AU,P,P,AU,P
2014-09-11,AU,AU,P,AU,AU,P,AU,AU,AU,P
2014-09-15,P,P,P,P,AU,P,P,P,AU,P
2014-09-17,P,P,P,P,AU,AU,P,P,AU,P
The columns are each period,and each one has an indicator of my presence. My question is, is it possible to turn that into something like this using only sqlite?
Date,P,AU,T*,N/A
2014-09-03,6,3,1,0
2014-09-04,6,1,1,2
2014-09-05,8,2,0,0
2014-09-09,7,3,0,0
2014-09-11,3,7,0,0
2014-09-15,8,2,0,0
2014-09-17,7,3,0,0
2014-09-19,9,1,0,0
Counting each occurence of a value across the row.
Something like this:
select date,
case when one = 'p' then 1 else 0 end +
case when two = 'p' then 1 else 0 end +
...
case when dee = 'p' then 1 else 0 end as p,
case when one = 'au' then 1 else 0 end +
case when two = 'au' then 1 else 0 end +
...
case when dee = 'au' then 1 else 0 end as au,
...
from table

Counting how many data that exist [SQL]

im not sure about this question is already asked by anyone else or not yet because this is actually easy but my head is just still can't see the way out of this problem.
this is just like how many times that we do sampling at the material.
SELECT
TABLE01.MATERIAL_NO,
TABLE01.Sample_Tempt1,
TABLE01.Sample_Tempt2,
TABLE01.Sample_Tempt3,
TABLE01.Sample_Tempt4,
TABLE01.Sample_Tempt5
FROM
TABLE01
is it possible to create another column to show count of sample_tempt times?
i mean, if the tempt1 tempt2 data are exist, the column shows 2, when tempt2, tempt4 and tempt5 data are exist, the column show 3. and so on.
Thank you for helping me ^^
Sample :
Material no | Sample_Tempt1 | Sample_Tempt2 | Sample_Tempt3 | Sample_Tempt4 | Sample_Tempt5 |
PO1025 120 150 102
PO1026 122
For the PO1025, i want to create new column that generate "3" because the sample data that exist is only 3, for the PO1026 i want it generate "1" since the sample data that exist is only "1". quite simple right?
If "by exist" you mean "value is not NULL", then you can count the number of non-NULL values in each row as:
SELECT t1.MATERIAL_NO,
t1.Sample_Tempt1, t1.Sample_Tempt2, t1.Sample_Tempt3, t1.Sample_Tempt4, t1.Sample_Tempt5,
((case when t1.sample_temp1 is not null then 1 else 0 end) +
(case when t1.sample_temp2 is not null then 1 else 0 end) +
(case when t1.sample_temp3 is not null then 1 else 0 end) +
(case when t1.sample_temp4 is not null then 1 else 0 end) +
(case when t1.sample_temp5 is not null then 1 else 0 end)
) as NumTempts
FROM TABLE01 t1;
Note that I introduced a table alias. This makes the query easier to write and to read.

Divide by zero error encountered even when divider is set as NOT NULL

I've come across the error "Divide by zero error encountered" when running this query.
> SUM(CASE WHEN EML_DateSent IS NOT NULL THEN 1 ELSE 0 END) AS [Sends],
(SUM(CASE WHEN EML_DateViewed IS NOT NULL OR EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)) * 100 / SUM((CASE WHEN EML_Datesent IS NOT NULL THEN 1 ELSE 0 END)) AS [Views %],
(SUM(CASE WHEN EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)) * 100 / SUM((CASE WHEN EML_DateViewed IS NOT NULL OR EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)) AS [Clicks %]
Its an edited existing stored procedure that now calculates percentages , any quick fix ?
Try using a max/maxium statement depending on what provider you are using.
/ MAX(SUM((CASE WHEN EML_DateViewed IS NOT NULL OR EML_DateClicked IS NOT NULL THEN 1 ELSE 0 END)), 1)
This will use your sum if it has a value, if it is zero the division will use 1 instead.
You don't show the grouping criteria but it's obvious at least one of the groups has one of the dates set to NULL over the entire group. First, you don't have to put all that logic in a sum function. The count function does that, counting all the not null values, ignoring the null values. Where that doesn't work is where you're checking both dates, but that is solved by a simple coalesce. You want a count of where one date or the other or both are not null. There you can play a little trick:
select count(EMS_DateSent) AS Sends,
count(coalesce(EMS_DateViewed, EMS_DateClicked)) * 100
/ case count(EMS_Datesent)
when 0 then 1000000
else count(EMS_Datesent)
end as "Views %",
count(EMS_DateClicked) * 100
/ case count(coalesce(EMS_DateViewed, EMS_DateClicked))
when 0 then 1000000
else count(coalesce(EMS_DateViewed, EMS_DateClicked))
end AS "Clicks %"
from EML
group by whatever;
If the divisor is 0 (all nulls over the group), I have set to a large number so you get a very small answer. But this must be large relative to actual counts in your application so adjust as needed.

mysql: Average over multiple columns in one row, ignoring nulls

I have a large table (of sites) with several numeric columns - say a through f. (These are site rankings from different organizations, like alexa, google, quantcast, etc. Each has a different range and format; they're straight dumps from the outside DBs.)
For many of the records, one or more of these columns is null, because the outside DB doesn't have data for it. They all cover different subsets of my DB.
I want column t to be their weighted average (each of a..f have static weights which I assign), ignoring null values (which can occur in any of them), except being null if they're all null.
I would prefer to do this with a simple SQL calculation, rather than doing it in app code or using some huge ugly nested if block to handle every permutation of nulls. (Given that I have an increasing number of columns to average over as I add in more outside DB sources, this would be exponentially more ugly and bug-prone.)
I'd use AVG but that's only for group by, and this is w/in one record. The data is semantically nullable, and I don't want to average in some "average" value in place of the nulls; I want to only be counting the columns for which data is there.
Is there a good way to do this?
Ideally, what I want is something like UPDATE sites SET t = AVG(a*#a_weight,b*#b_weight,...) where any null values are just ignored and no grouping is happening.
EDIT: What I ended up using, based on van's and adding in correct weighted averages (assuming that a has already been normalized as needed, in this case to a float 0-1 (1 = better):
UPDATE sites
SET t = (#a_weight * IFNULL(a, 0) + ...) / (IF(a IS NULL, 0, #a_weight) + ...)
WHERE (IF(a IS NULL, 0, 1) + ...) > 0
UPDATE sites
--// TODO: you might need to round it depending on your type
SET t =(COALESCE(a, 0) +
COALESCE(b, 0) +
COALESCE(c, 0) +
COALESCE(d, 0) +
COALESCE(e, 0) +
COALESCE(f, 0)
) /
((CASE WHEN a IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN b IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN c IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN d IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN e IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN f IS NULL THEN 0 ELSE 1 END CASE)
)
WHERE 0<>((CASE WHEN a IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN b IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN c IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN d IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN e IS NULL THEN 0 ELSE 1 END CASE) +
(CASE WHEN f IS NULL THEN 0 ELSE 1 END CASE)
)
You could use COALESCE also in the other parts, but this will not handle the case when you have a rating with value 0 properly because it will be excluded. The WHERE clause avoids DivideByZero, but you might need to have additional UPDATE statement to handle this case, if there is no rating for the entry.