Oracle View problem with Select and division by zero - sql

I want to create an view in Oracle which calculates an "utilization rate in percent".
AS SELECT
sw.SWITCH_ID,
sw.ASSET_ID,
sw.SYSTEMNAME,
sw.MAX_INSTALLABLE_PORTS,
sw.INSTALLED_PORTS,
sw.USED_PORTS,
(sw.INSTALLED_PORTS/sw.MAX_INSTALLABLE_PORTS)*100 AS UTIL_INSTALLED_PORTS,
sw.RES_INFRASTRUCTURE_PORTS,
sw.USED_INFRASTRUCTURE_PORTS,
sw.FREE_INFRASTRUCTURE_PORTS,
(sw.INSTALLED_PORTS/sw.MAX_INSTALLABLE_PORTS)*100 AS UTIL_INFRASTRUCTURE_PORTS,
sw.RESERVED_DEVICE_PORTS,
sw.USED_DEVICE_PORTS,
sw.FREE_DEVICE_PORTS,
(sw.FREE_DEVICE_PORTS/sw.RESERVED_DEVICE_PORTS)*100 AS UTIL_DEVICE_PORTS,
sw.RUN_DATE
Problem: sometimes sw.INSTALLED_PORTS or sw.MAX_INSTALLABLE_PORTS can be NULL (same for other UTIL Rows).
Is there any nice way to do something like:
if (sw.INSTALLED_PORTS or sw.MAX_INSTALLABLE_PORTS == null)
UTIL_INSTALLABLE_PORTS = null
else (sw.INSTALLED_PORTS/sw.MAX_INSTALLABLE_PORTS)*100 AS UTIL_INSTALLABLE_PORTS,

or a little shorter:
sw.INSTALLED_PORTS/NULLIF(sw.MAX_INSTALLABLE_PORTS,0)
Regards,
Rob.

Divizion by NULL is not the same as divizion by zero (as you reference the problem in the title).
select 1/null from dual = null
select null/null from dual = null
So you'll automatically get what you want by (sw.INSTALLED_PORTS/sw.MAX_INSTALLABLE_PORTS)*100.
I think, the problem is when sw.MAX_INSTALLABLE_PORTS is zero. In this case you can use the following:
case
when sw.MAX_INSTALLABLE_PORTS = 0 then null
else (sw.INSTALLED_PORTS/sw.MAX_INSTALLABLE_PORTS)*100
end

CASE
WHEN sw.INSTALLED_PORTS IS NULL OR sw.MAX_INSTALLABLE_PORTS IS NULL THEN NULL
ELSE (sw.INSTALLED_PORTS/sw.MAX_INSTALLABLE_PORTS)*100
END UTIL_INFRASTRUCTURE_PORTS

You could use COALESCE() which returns the first non-null from its arguments, like :
(COALESCE(sw.INSTALLED_PORTS, 0)/COALESCE(sw.MAX_INSTALLABLE_PORTS,1))*100
AS UTIL_DEVICE_PORTS
Note that I used 1 as the coalesce default in the denominator, you don't want a DIVIDE/0 instead.

Related

using a searched case in where condition

I recently got this stored procedure where it is using a variable called #marketCode. The application is passing either South or null for this variable.
In my select statement, I want to retrieve every market except South if the application passes null.
and marketCode =! 'South'
If the application passes a non null value, then I will use that one instead.
and marketCode = 'North'
How can I join these two into one case?
AND marketCode = (case #marketCode when null then <..not equal to 'South'..>
else <..equal to 'South'..> end )
You should be able to do this with an OR condition:
...
AND (
(#marketCode IS NULL AND marketCode <> 'South')
OR
(#marketCode IS NOT NULL AND marketCode = #marketCode)
)

Use CASE statement to test values in table 1, set values in table 2

I am new to SQL, so if you could include in answer correct syntax (for PostgreSQL) that will be great.
I have two tables
table 1 "geo_temp", with columns [geo_type1] [geo_type2]....[geo_type6] [geo_typeR];
table 2 "geo_summary", with column [geo].
Here what I want to do,
CASE
WHEN (geo_type1 NOTNULL) THEN (geo = geo_type1)
WHEN (geo_type2 NOTNULL) THEN (geo = geo_type2)
...
ELSE (geo = geo_typeR)
Your help is much appreciated. Thanks.
Use the coalesce() function:
geo = coalesce(geo1, geo2, ...,geoR)
coalesce() returns the first non-null value found in the list, which is what your intention is.
As an update statement:
update geo_summary set
geo = (select coalesce(geo1, geo2, ...,geoR)
from geo_temp
where geo_temp.id = geo_summary.id)
I am assuming you want to use this in a WHERE or ON Clause.
You could do something like...
geo =
CASE
WHEN (geo_type1 is NOT NULL) THEN geo_type1
WHEN (geo_type2 is NOT NULL) THEN geo_type2
...
ELSE geo_typeR
END

SQL: field = other_field returns false even if they are identical (NULL values)

I have a difficulty because when comparing two fields in a subquery, although the fields are identical i.e. they both have NULL values, the comparison returns a FALSE result
Therfore NULL = NULL is returning FALSE
Now I know that NULLs are supposed to be compared with the IS operator, however when I compare two fields how am I supposed to know they contain a null? I need to compare two fields for identical data both if the values are NULL or not.
Consider this SQL:
SELECT
*
FROM
fts.fts_customers_data_50360001
WHERE
fts.fts_customers_data_50360001.record_type = 15
AND
fts.fts_customers_data_50360001.mid = 103650360001
AND NOT EXISTS
(
SELECT
fts.temp_fees_50360001.record_type
FROM
fts.temp_fees_50360001
WHERE
fts.temp_fees_50360001.record_type = fts.fts_customers_data_50360001.record_type
AND
fts.temp_fees_50360001.merch_id = fts.fts_customers_data_50360001.mid
AND
fts.temp_fees_50360001.fee_curr = fts.fts_customers_data_50360001.currency
AND
fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
AND
fts.temp_fees_50360001.tran_type = fts.fts_customers_data_50360001.fee_type
AND
fts.temp_fees_50360001.area = fts.fts_customers_data_50360001.region
AND
fts.temp_fees_50360001.srvc_type = fts.fts_customers_data_50360001.card_type
);
In the query above,
fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
both have NULL values inside but the comparison returns false .. too bad
ANY IDEAS WOULD BE MUCH APPRECIATED
As the others have pointed out, NULL cannot be compared with NULL.
In Postgres you can shorten your expressions by using the operator IS DISTINCT FROM which is a null-safe replacement for <>. In your case you'd need to use IS NOT DISTINCT FROM to compare for equality (looks a bit the wrong way round but unfortunately there is no corresponding IS EQUAL TO defined in the SQL standard).
From the manual:
Ordinary comparison operators yield null (signifying "unknown"), not true or false, when either input is null. For example, 7 = NULL yields null, as does 7 <> NULL. When this behavior is not suitable, use the IS [ NOT ] DISTINCT FROM constructs:
So, instead of
(fts.temp_fees_50360001.record_type = fts.fts_customers_data_50360001.record_type
OR (fts.temp_fees_50360001.record_type IS NULL
AND fts.fts_customers_data_50360001.record_type IS NULL)
)
you can use:
(fts.temp_fees_50360001.record_type IS NOT DISTINCT FROM fts.fts_customers_data_50360001.record_type)
to handle NULL values automatically. The condition looks a bit strange if you want to compare for equality but it still is quite short.
First of all, use aliases for your tables, your query will be MUCH more readable:
select *
from fts.fts_customers_data_50360001 as d
where
d.record_type = 15 and
d.mid = 103650360001 and
not exists
(
select *
from fts.temp_fees_50360001 as f
where
f.record_type = d.record_type and
f.merch_id = d.mid and
f.fee_curr = d.currency and
f.card_scheme = d.card_scheme and
f.tran_type = d.fee_type and
f.area = d.region and
f.srvc_type = d.card_type
)
As for your question, there's several ways to do this, for example, you can use syntax like this:
...
(
f.card_scheme is null and d.card_scheme is null or
f.card_scheme = d.card_scheme
)
...
Or use coalesce with some value that couldn't be stored in your column:
...
coalesce(f.card_scheme, -1) = coalesce(d.card_scheme, -1)
...
Recently I also like using exists with intersect for this type of comparisons:
...
exists (select f.card_scheme, f.tran_type intersect select d.card_scheme, d.tran_type)
...
Just a side note - you have to be careful when writing queries like this and check query plans to be sure your indexes are used.
In SQL, null is never equal to null. The only way to get a true result for a comparison with null is via the special tests:
IS NULL
IS NOT NULL
In your case, you must cater specifically for the "two nulls" case being considered equal:
AND (fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
OR (fts.temp_fees_50360001.card_scheme IS NULL
AND fts.fts_customers_data_50360001.card_scheme IS NULL)
)
There's no getting around dealing with it (although there are a few variations).
The following inner SELECT works (but I give no guarantee regarding performance):
SELECT
fts.temp_fees_50360001.record_type
FROM
fts.temp_fees_50360001
WHERE
(fts.temp_fees_50360001.record_type = fts.fts_customers_data_50360001.record_type
OR (fts.temp_fees_50360001.record_type IS NULL AND fts.fts_customers_data_50360001.record_type IS NULL))
AND
(fts.temp_fees_50360001.merch_id = fts.fts_customers_data_50360001.mid
OR (fts.temp_fees_50360001.merch_id IS NULL AND fts.fts_customers_data_50360001.mid IS NULL))
AND
(fts.temp_fees_50360001.fee_curr = fts.fts_customers_data_50360001.currency
OR (fts.temp_fees_50360001.fee_curr IS NULL AND fts.fts_customers_data_50360001.currency IS NULL))
AND
(fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
OR (fts.temp_fees_50360001.card_scheme IS NULL AND fts.fts_customers_data_50360001.card_scheme IS NULL))
AND
(fts.temp_fees_50360001.tran_type = fts.fts_customers_data_50360001.fee_type
OR (fts.temp_fees_50360001.tran_type IS NULL AND fts.fts_customers_data_50360001.fee_type IS NULL))
AND
(fts.temp_fees_50360001.area = fts.fts_customers_data_50360001.region
OR (fts.temp_fees_50360001.area IS NULL AND fts.fts_customers_data_50360001.region IS NULL))
AND
(fts.temp_fees_50360001.srvc_type = fts.fts_customers_data_50360001.card_type
OR (fts.temp_fees_50360001.srvc_type IS NULL AND fts.fts_customers_data_50360001.card_type))

Return 0 if field is null in MySQL

In MySQL, is there a way to set the "total" fields to zero if they are NULL?
Here is what I have:
SELECT uo.order_id, uo.order_total, uo.order_status,
(SELECT SUM(uop.price * uop.qty)
FROM uc_order_products uop
WHERE uo.order_id = uop.order_id
) AS products_subtotal,
(SELECT SUM(upr.amount)
FROM uc_payment_receipts upr
WHERE uo.order_id = upr.order_id
) AS payment_received,
(SELECT SUM(uoli.amount)
FROM uc_order_line_items uoli
WHERE uo.order_id = uoli.order_id
) AS line_item_subtotal
FROM uc_orders uo
WHERE uo.order_status NOT IN ("future", "canceled")
AND uo.uid = 4172;
The data comes out fine, except the NULL fields should be 0.
How can I return 0 for NULL in MySQL?
Use IFNULL:
IFNULL(expr1, 0)
From the documentation:
If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2. IFNULL() returns a numeric or string value, depending on the context in which it is used.
You can use coalesce(column_name,0) instead of just column_name. The coalesce function returns the first non-NULL value in the list.
I should mention that per-row functions like this are usually problematic for scalability. If you think your database may get to be a decent size, it's often better to use extra columns and triggers to move the cost from the select to the insert/update.
This amortises the cost assuming your database is read more often than written (and most of them are).
None of the above answers were complete for me.
If your field is named field, so the selector should be the following one:
IFNULL(`field`,0) AS field
For example in a SELECT query:
SELECT IFNULL(`field`,0) AS field, `otherfield` FROM `mytable`
Hope this can help someone to not waste time.
You can try something like this
IFNULL(NULLIF(X, '' ), 0)
Attribute X is assumed to be empty if it is an empty String, so after that you can declare as a zero instead of last value. In another case, it would remain its original value.
Anyway, just to give another way to do that.
Yes IFNULL function will be working to achieve your desired result.
SELECT uo.order_id, uo.order_total, uo.order_status,
(SELECT IFNULL(SUM(uop.price * uop.qty),0)
FROM uc_order_products uop
WHERE uo.order_id = uop.order_id
) AS products_subtotal,
(SELECT IFNULL(SUM(upr.amount),0)
FROM uc_payment_receipts upr
WHERE uo.order_id = upr.order_id
) AS payment_received,
(SELECT IFNULL(SUM(uoli.amount),0)
FROM uc_order_line_items uoli
WHERE uo.order_id = uoli.order_id
) AS line_item_subtotal
FROM uc_orders uo
WHERE uo.order_status NOT IN ("future", "canceled")
AND uo.uid = 4172;

Using a CASE statement in HQL select

Is there any way to do the following in HQL:
SELECT
case when flag = true then SUM(col1) else SUM(col2)
FROM
myTable
I guess you can (3.6, 4.3) [inline edit] ...for where-clauses:
"Simple" case, case ... when ... then ... else ... end, and "searched" case, case when ... then ... else ... end
Apparently the ability to do this was added in 3.0.4, with the limitation that you cannot use sub-selects in the else clause.
See Hibernate-Forum: https://forum.hibernate.org/viewtopic.php?t=942197
Answer from Team (Gavin):
case is supported in the where clause, but not in the select clause in HB3.
And seen in JIRA with State "Unresolved".
Below you can find a working query (hibernate on postgresql) that uses 2 case statements to replace a boolean value with the corresponding textual representation.
SELECT
CASE ps.open WHEN true THEN 'OPEN'
else 'CLOSED' END,
CASE ps.full WHEN true THEN 'FULL'
else 'FREE' END,
ps.availableCapacity
FROM ParkingState as ps
I facing the same problem in HQL then I solved the following query is
select CONCAT(event.address1,', ', CASE WHEN event.address2 IS NULL THEN '' ELSE concat(event.address2,', ') END, event.city from EventDetail event where event.startDate>=:startDate and event.endDate<=:endDate;
We use hibernate HQL query extensively and I think finally there is a hackish way of doing such a thing :
Assuming we originally had a query of
i2.element.id = :someId
Then decided to expand this to be something like this:
((i.element.id = :someId and i2.element.id=:someId) or (i2.element.id = :someId))
But there was an issue where we want it to only lookup for this based on classType so a case statement:
(case when type(i)=Item then
((i.element.id = :someId and i2.element.id=:someId) or (i2.element.id = :someId))
else
i.element.id = :someId
end)
Above will not work you could make an easy version of above work by doing:
(case when type(i)=Item then
i2.element.id
else
i.element.id
end)=:elementId
But this does not actually do what we need it to do, we want it to do exact above query, so knowing you can assign a variable at the end of a case statement in there where bit of HQL:
(
(
(case when
type(r)=Item then
i.element.id
else
i.element.id end) = :elementId
and
(case when
type(r)=Item then
i2.element.id
else
i.element.id end) = :elementId
)
or
(case when
type(r)=Item then
i2.element.id
else
i.element.id end) = :elementId
)
I have managed to make the query now work based on case statement, sure it is a lot more long winded but actually does the same as the first instance
This is an example using a string comparison in the condition:
SELECT CASE f.type WHEN 'REMOVE'
THEN f.previousLocation
ELSE f.currentLocation
END
FROM FileOperation f