Using NVL for multiple columns - Oracle SQL - sql

Good morning my beloved sql wizards and sorcerers,
I am wanting to substitute on 3 columns of data across 3 tables. Currently I am using the NVL function, however that is restricted to two columns.
See below for an example:
SELECT ccc.case_id,
NVL (ccvl.descr, ccc.char)) char_val
FROM case_char ccc, char_value ccvl, lookup_value lval1
WHERE
ccvl.descr(+) = ccc.value
AND ccc.value = lval1.descr (+)
AND ccc.case_id IN ('123'))
case_char table
case_id|char |value
123 |email| work_email
124 |issue| tim_
char_value table
char | descr
work_email | complaint mail
tim_ | timeliness
lookup_value table
descr | descrlong
work_email| xxx#blah.com
Essentially what I am trying to do is if there exists a match for case_char.value with lookup_value.descr then display it, if not, then if there exists a match with case_char.value and char_value.char then display it.
I am just trying to return the description for 'issue'from the char_value table, but for 'email' I want to return the descrlong from the lookup_value table (all under the same alias 'char_val').
So my question is, how do I achieve this keeping in mind that I want them to appear under the same alias.
Let me know if you require any further information.
Thanks guys

You could nest NVL:
NVL(a, NVL(b, NVL(c, d))
But even better, use the SQL-standard COALESCE, which does take multiple arguments and also works on non-Oracle systems:
COALESCE(a, b, c, d)

How about using COALESCE:
COALESCE(ccvl.descr, ccc.char)

Better to Use COALESCE(a, b, c, d) because of below reason:
Nested NVL logic can be achieved in single COALESCE(a, b, c, d).
It is SQL standard to use COALESCE.
COALESCE gives better performance in terms, NVL always first calculate both of the queries used and then compare if the first value is null then return a second value. but in COALESCE function it checks one by one and returns response whenever if found a non-null value instead of executing all the used queries.

Related

Is there a Python fn that mimics nvl() or can I use SQL to do it?

I need to join two tables using python on an nvl type fn because we have one table where, based on the part type, we only put in the first 7 characters.
So far I have not found a simple way to do this in python.
Is there a function that will do this, or another easy way to achieve it?
Thank you in advance
I joined on part_number, removed where the other table's fields were NaN, then joined as a new table on the substring, then appended the tables together. And ended up with the wrong number of rows.
left join on nvl(nvl(thistable.part_number, substr(thistable.part_number, 1, 7)),'not in defn table') = part_number.othertable
Output might be like this:
thistable.part_number othertable.description
abc123 real part
def456 another real part
1234567-02 part stored as 1234567 in othertable
koue49c not in defn table
I think you want coalesce():
on coalesce(thistable.part_number, substr(thistable.part_number, 1, 7), 'not in defn table') = part_number.othertable
coalesce() takes multiple arguments, so you don't need to nest the function calls.

Missing records if add "NOT IN"

I have a table of doctor names and states.
f_name | l_name | state
MICHAEL | CRANE |
HAL | CRANE | MD
THOMAS | ROMINA | DE
And so on.
What I want is to get all doctors that are NOT in MD. However, if I write this expression I'm missing those with NULL values for state.
SELECT *
FROM doctors
WHERE state NOT IN ('MD')
I don't understand the issue. I was able to fix it by adding
OR state IS NULL
Obviously it has something to due with NOT IN (or IN) not handling NULL. Can anyone explain this for me? Is there an alternative for what I was trying to do?
Thanks
Yes, there is an alternative - you would use the NVL() function (or COALESCE() if you want to stick to the ANSI standard):
SELECT * FROM doctors
WHERE NVL(state, '##') NOT IN ('MD')
However you don't really need to use NOT IN here - it's only necessary when you have multiple values, e.g.:
SELECT * FROM doctors
WHERE NVL(state, '##') NOT IN ('MD','PA')
With one value you can just use = (or in this case, != or <>):
SELECT * FROM doctors
WHERE NVL(state, '##') != 'MD'
In Oracle SQL, NULL can't be compared to other values (not even other NULLs). So WHERE NULL = NULL, for example, will return zero rows. You do NULL comparisons with IS NULL and IS NOT NULL.
As noted already, you don't know that Michael Crane's state isn't Maryland. It's NULL, which can be read as representing "don't know". It might be Maryland, or it might not be. NOT IN ('MD') only finds those values known not to be 'MD'.
If you have a filter WHERE x, you can use MINUS to find exactly those records where x is not true (where x is either false or unknown).
select *
from doctors
minus
select *
from doctors
where state in ('MD');
This has one big advantage over anything involving IS NULL or NVL: it's immediately obvious exactly which records you don't want to see. You don't have to worry about accidentally missing one case where NULL isn't covered in your condition, and you don't have to worry about records that happen to match whatever dummy value you use with NVL.
It's generally not good for performance on Oracle, accessing the table twice, but for one-off queries, depending on the table size, the time saved writing the query can be more than the added execution time.
Inside database, null are not physical string values("null"), it simply says no value. So, if you compare NULLs to anything, it will not be equal or not equal. Even, two NULL's are not equal. You can only check whetherr a value is NULL or not but you can't compare it to other values.

PostgreSQL: How to access column on anonymous record

I have a problem that I'm working on. Below is a simplified query to show the problem:
WITH the_table AS (
SELECT a, b
FROM (VALUES('data1', 2), ('data3', 4), ('data5', 6)) x (a, b)
), my_data AS (
SELECT 'data7' AS c, array_agg(ROW(a, b)) AS d
FROM the_table
)
SELECT c, d[array_upper(d, 1)]
FROM my_data
In the my data section, you'll notice that I'm creating an array from multiple rows, and the array is returned in one row with other data. This array needs to contain the information for both a and b, and keep two values linked together. What would seem to make sense would be to use an anonymous row or record (I want to avoid actually creating a composite type).
This all works well until I need to start pulling data back out. In the above instance, I need to access the last entry in the array, which is done easily by using array_upper, but then I need to access the value in what used to be the b column, which I cannot figure out how to do.
Essentially, right now the above query is returning:
"data7";"(data5,6)"
And I need to return
"data7";6
How can I do this?
NOTE: While in the above example I'm using text and integers as the types for my data, they are not the actual final types, but are rather used to simplify the example.
NOTE: This is using PostgreSQL 9.2
EDIT: For clarification, Something like SELECT 'data7', 6 is not what I'm after. Imagine that the_table is actually pulling from database tables and not the WITH statement the I put in for convenience, and I don't readily know what data is in the table.
In other words, I want to be able to do something like this:
SELECT c, (d[array_upper(d, 1)]).b
FROM my_data
And get this back:
"data7";6
Essentially, once I've put something into an anonymous record by using the row() function, how do I get it back out? How do I split up the 'data5' part and the 6 part so that they don't both return in one column?
For another example:
SELECT ROW('data5', 6)
makes 'data5' and 6 return in one column. How do I take that one column and break it back into the original two?
I hope that clarifies
If you can install the hstore extension:
with the_table as (
select a, b
from (values('data1', 2), ('data3', 4), ('data5', 6)) x (a, b)
), my_data as (
select 'data7' as c, array_agg(row(a, b)) as d
from the_table
)
select c, (avals(hstore(d[array_upper(d, 1)])))[2]
from my_data
;
c | avals
-------+-------
data7 | 6
This is just a very quick throw together around a similarish problem - not an answer to your question. This appears to be one direction towards identifying columns.
with x as (select 1 a, 2 b union all values (1,2),(1,2),(1,2))
select a from x;

Checking Range in Comma Separated Values [SQL Server 2008]

I have a table with following structure
ID FirstName LastName CollectedNumbers
1 A B 10,11,15,55
2 C D 101,132,111
I want a boolean value based on CollectedNumber Range. e.g. If CollectedNumbers are between 1 and 100 then True if Over 100 then False. Can anyone Suggest what would be best way to accomplish this. Collected Numbers won't be sorted always.
It so happens that you have a pretty simple way to see if values are 100 or over in the list. If such a value exists, then there are at least three characters between the commas. If the numbers are never more than 999, you could do:
select (case when ','+CollectedNumbers+',' not like '%,[0-9][0-9][0-9]%' then 1
else 0
end) as booleanflag
This happens to work for the break point of 100. It is obviously not a general solution. The best solution would be to use a junction table with one row per id and CollectedNumber.
Just make a function, which will return true/False, in the database which will convert the string values(10,11,15,55) into a table and call that function in the Selection of the Query like this
Select
ID, FirstName, LastName,
dbo.fncCollectedNumbersResult(stringvalue) as Result
from yourTableName
I think the easiest you can do is build a C# function and use the builtin sqlclr to load it as a custom function you can then call.
Inside the C# function, you can then sort your numbers and make simple logic to return your true/false.

Parameters in Microsoft Access

I'm really confused with how parameters work in Microsoft Access. I know that parameters are supposed to be used to allow a user to type in values when the query is run - instead of having to modify the query for each instance.
So, let's use the following example.
SELECT countyTable.countyName, Sqr((69.1*(46.47-avgLatitude))^2+(69.1*(-90.17-avgLongitude)*Cos(avgLatitude/57.3))^2) as Distance
FROM countyTable
WHERE ((([avgLatitude]-5)<46.47) AND (([avgLatitude]+5)>46.47) AND (([avgLongitude]-5)<-90.17) AND (([avgLongitude]+5)>-90.17))
ORDER BY Sqr((69.1*(46.47-avgLatitude))^2+(69.1*(-90.17-avgLongitude)*Cos(avgLatitude/57.3))^2), countyTable.countyName
1) I am SELECTing a column that contains the SQR function. I also have that column named as 'Distance'. However, when I try to ORDER BY on said column - and refer to it as 'Distance' - it asks for a value instead of sorting on that column. The only way I can get the query to ORDER BY is to duplicate the expression from the SELECT line. This seems unnecessary.
2) Right now, I have some values hard-coded in. I could care less about the values '57.3' and '69.1' However, for '46.47' I would like to replace with 'x2' and -90.17 with 'y2'. How I've been trying to write this with parameters, Access asks for values for each instance of 'x2' and 'y2'. This doesn't help me at all, so I have them hardcoded in.
Any help at all? Thanks!
1) I am SELECTing a column that contains the SQR function. I also have that column named as 'Distance'. However, when I try to ORDER BY on said column - and refer to it as 'Distance' - it asks for a value instead of sorting on that column. The only way I can get the query to ORDER BY is to duplicate the expression from the SELECT line. This seems unnecessary.
Yes Access does a poor job. Every real DBMS now supports ordering by the column alias created in the SELECT clause. To do this in Access, you can either do what you are doing (repeat the expression) or subquery it, e.g.
select a,b,c
from (
select a, b, a+b as C
from sometable
) AS SUBQUERIED
order by c
2) How I've been trying to write this with parameters, Access asks for values for each instance of 'x2' and 'y2'.
You're doing it wrong. Access should prompt only once. If you have a query like this
select a, b, a+b as C
from sometable
where a > [x] and y > [x]
It will see both [x]'s as being the same - and only one prompt for both. Just make sure they are spelt exactly the same.
If you wanted something like this simplified example:
SELECT
countyTable.countyName,
Sqr((69.1*(46.47-avgLatitude))^2+(69.1*(-90.17-avgLongitude)*Cos(avgLatitude/57.3))^2) as Distance
FROM countyTable
ORDER BY Distance;
For the ORDER BY you can reference that complex Distance expression by its ordinal position in the field list.
SELECT
countyTable.countyName,
Sqr((69.1*(46.47-avgLatitude))^2+(69.1*(-90.17-avgLongitude)*Cos(avgLatitude/57.3))^2) as Distance
FROM countyTable
ORDER BY 2;
That method is supported at least since Jet 4 (Access 2000), and also by the newer ACE database engine.