I'm trying to understand how Oracle handles NULL values. This is really more of a request for confirmation of what I've already discovered, to make sure I haven't overlooked something obvious. Below are scripts to confirm that Oracle handles NULL values the same as it handles a blank string. If such is the case, then pray tell why would any Oracle database be developed to ever allow a field to be NULL, other than to introduce obfuscation to an index when querying against it (i.e. ... WHERE NVL(FieldName,'Default Value') != 'test')???
-- Returns only 2 rows
-- Oracle doesn't differentiate from blank strings and NULL values
SELECT NULL AS Test_Nulls from dual
UNION
SELECT '' AS Test_Nulls from dual
UNION
SELECT 'Test' AS Test_Nulls from dual;
-- Returns "X"
-- Further proof Oracle doesn't differentiate from blank strings and NULL values
SELECT 'X' AS Test_Nulls
FROM dual
WHERE '' is NULL;
-- Returns "X"
-- Even further proof Oracle doesn't differentiate from blank strings and NULL values
SELECT NVL('','X') AS Test_Nulls
FROM dual;
-- Returns 3 rows
-- Oracle recognizes <Carriage Return> + <Line Feed>
SELECT '' AS Returned from dual
UNION
SELECT '
' AS Returned FROM dual
UNION
SELECT 'Test' AS Returned FROM dual;
Simple Answer: For any new database design work I do in Oracle, I will set all my varchar2 and nvarchar2 and char2 and nchar2 fields to not allow NULL and to have a default value of an empty string. I have always been suspicious of the naming convention of adding a "2" suffix, and the fact that it is not a common field type to other database platforms. To me, it seems like a last-minute desperate means that Oracle handled a situation in their early days like, "Oh well, ... we can't make it right, so we'll just give it a new name!" That's my 2-cents on this matter.
Related
i am creating a cursor on a query getting all the columns from a table. in a loop i am using each column and printing it out. I wanted to know any easy way to check whether a column has any value or not. if a column in a row is null, put "NULL". I know I can use if condition to check each and every column in a row from the cursor in a for loop. but I have alot of columns, so is there an easy way of doing that?
Rather than handle this requirement within PL/SQL, you should consider doing it in the cursor itself. In general, the more you can do in your SQL the better (The optimizer will handle the performance). Using lots of PL/SQL logic especially unnecessary if/else conditions in a loop will benefit you if you are processing a lot of data.
In your cursor you can simply:
CURSOR c1 IS
SELECT NVL(column, 'NULL')
FROM dual;
There are plenty of in-built SQL functions that will handle this for you. Above, I used NVL, but you can use COALESCE, CASE, etc.
CASE example (note - if the column is not datatype VARCHAR then you will need to apply some datatype conversion to avoid any errors):
CURSOR c1 IS
SELECT (CASE
WHEN column IS NULL THEN
'NULL'
ELSE
column
END)
FROM dual;
COALESCE example:
CURSOR c1 IS
SELECT COALESCE(column, 'NULL')
FROM dual;
It's worth noting that NVL is a binary function, meaning only one column/value can be tested, however COALESCE can test multiple columns/values at the same time like below:
SELECT COALESCE(column1, column2, column3, 'NULL')
FROM YOUR_TABLE;
In the case that column1 value is NULL and column2 is NOT NULL then column2 value will be returned.
If column1 and column2 values are both NULL then column3 value will be returned.
If column1, column2, column3 are all NULL then the value 'NULL' is returned.
Am working with an Oracle 11g db in which I need to run a query that returns all rows with values in a specific format.
I.e., I need to find all rows with a value like 'abc1234'. The actual values aren't important....what I need is to find all rows with the first 3 characters being alpha and the subsequent 4 characters all being numeric.
Any input would be much appreciated.
Might not be exact since I don't have an Oracle server handy to test but something like this should get you started in the right direction:
SELECT * FROM your_table WHERE REGEXP_LIKE(your_column, '([a-z]\3[0123456789]\4', 'i')
This will check all rows in your table where the specified column has any alphabet character A to Z three times followed by 4 numbers and return that list. The 'i' at the end just says ignore case on the alphabet part. You can choose to not have that option if you so wish.
Here are a couple links as well with more information on the regexp_like syntax:
Oracle SQL - REGEXP_LIKE contains characters other than a-z or A-Z
https://docs.oracle.com/cd/B28359_01/server.111/b28286/conditions007.htm#SQLRF00501
Try it this will work definitely:
Let's take a sample example:
For example, create a sample table
CREATE TABLE bob (
empno VARCHAR2(10) NOT NULL,
ename VARCHAR2(15) NOT NULL,
ssn VARCHAR2(10) NOT NULL);
Insert data into that table
Insert into bob ( empno,ename,ssn) values ('abC0123458','zXe5378023','0pl4783202');
Insert into bob ( empno,ename,ssn) values ('0123abcdef','a12dldfddd','Rns0101010');
Query to select the all the columns
select * from bob
where length(trim(translate(substr(empno,1,3),'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',' '))) IS NULL
AND
length(trim(translate(substr(empno,4,4),'0123456789',' '))) IS NULL;
To do the same thing on multiple columns use union operator
select * from bob
where length(trim(translate(substr(empno,1,3),'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',' '))) IS NULL
AND
length(trim(translate(substr(empno,4,4),'0123456789',' '))) IS NULL;
union
select * from bob
where length(trim(translate(substr(ename,1,3),'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',' '))) IS NULL
AND
length(trim(translate(substr(ename,4,4),'0123456789',' '))) IS NULL;
Simple question:
How to print a field's type?
DESC TABLE_FOO.FIELD_FOO;
Results printing the whole table description.
How to print a specific field details with a command, i.e, without a SELECT?
There is no built-in way to do this, at least in any of the clients I've used. As mentioned in comments, describe is a client wrapper around a data dictionary query, and each client can implement it differently - SQL*Plus and SQL Developer seem to be slightly different, and no client is actually required to support this command at all.
Just for fun, if you really wanted to you could create a procedure to figure out and format the data type the same as desc, something like:
create or replace procedure col_data_type (p_table_name varchar2,
p_column_name varchar2)
as
l_data_type varchar2(30);
begin
select data_type
|| case when data_type = 'VARCHAR2' then '(' || data_length || ')' end
|| case when data_type = 'NUMBER' and data_precision is not null then
'(' || data_precision
|| case when data_scale is not null and data_scale > 0 then
',' || data_scale end
|| ')' end
into l_data_type
from user_tab_columns
where table_name = p_table_name
and column_name = p_column_name;
dbms_output.put_line(l_data_type);
end col_data_type;
/
Perhaps with more special formatting for other data types, but those are the obvious ones. You can then call that with execute. With a dummy table:
create table t42(i integer, n1 number, n2 number(10), n3 number(10,5),
v varchar2(10), c clob)
Then:
set serveroutput on
exec col_data_type('T42','I');
NUMBER
exec col_data_type('T42','N1');
NUMBER
exec col_data_type('T42','N2');
NUMBER(10)
exec col_data_type('T42','N3');
NUMBER(10,5)
exec col_data_type('T42','V');
VARCHAR2(10)
exec col_data_type('T42','C');
CLOB
Not entirely sure how useful that might be, or why you want to be able to do this at all. Also notice that it requires the client to be retrieving and displaying dbms_output buffer. You could make it a function instead, which puts you back to using a select, albeit a shorter one...
I don't believe it is possible to accomplish this without using a SELECT statement.
DESC OWNER.TABLE_NAME; is basically just running a query like this anyways (although not exactly the same, that depends on your client):
SELECT *
FROM ALL_TAB_COLS
WHERE OWNER = &theOwner
AND TABLE_NAME = &theTable;
If you want to only return a single column, you can do this:
SELECT *
FROM ALL_TAB_COLS
WHERE OWNER = &theOwner
AND TABLE_NAME = &theTable
AND COLUMN_NAME = &theColumn;
As #AlexPoole suggests, you could work around this by writing your own custom PROCEDURE or FUNCTION to return exactly what you need, but I believe the answer to the question "is there a built in command other than SELECT that does exactly what you need" is no, there is not.
I don't think you can do this without using a SELECT. You may be able to come up with some way to route the output of DESC to a file and then parse the file out to get what you want, but honestly - SELECT is going to be much easier.
Relational databases store the description of what they store in the database, where said description can be obtained in the same manner as any other information in the database, i.e. by using a SELECT to read it. The actual tables which store this are somewhat difficult to interpret, but happily Oracle has taken pity on us poor users and provided views which present this info in an easy-to-read manner. To get the type of a field you want to do a select one of the *_TAB_COLS views, where * is either USER, ALL, or DBA. The particular columns of interest are likely going to be COLUMN_NAME, DATA_TYPE, and DATA_LENGTH.
Share and enjoy.
Oracle 12 introduced nice feature (which should have been there long ago btw!) - identity columns. So here's a script:
CREATE TABLE test (
a INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
b VARCHAR2(10)
);
-- Ok
INSERT INTO test (b) VALUES ('x');
-- Ok
INSERT INTO test (b)
SELECT 'y' FROM dual;
-- Fails
INSERT INTO test (b)
SELECT 'z' FROM dual UNION ALL SELECT 'zz' FROM DUAL;
First two inserts run without issues providing values for 'a' of 1 and 2. But the third one fails with ORA-01400: cannot insert NULL into ("DEV"."TEST"."A"). Why did this happen? A bug? Nothing like this is mentioned in the documentation part about identity column restrictions. Or am I just doing something wrong?
I believe the below query works, i havent tested!
INSERT INTO Test (b)
SELECT * FROM
(
SELECT 'z' FROM dual
UNION ALL
SELECT 'zz' FROM dual
);
Not sure, if it helps you any way.
For, GENERATED ALWAYS AS IDENTITY Oracle internally uses a Sequence only. And the options on general Sequence applies on this as well.
NEXTVAL is used to fetch the next available sequence, and obviously it is a pseudocolumn.
The below is from Oracle
You cannot use CURRVAL and NEXTVAL in the following constructs:
A subquery in a DELETE, SELECT, or UPDATE statement
A query of a view or of a materialized view
A SELECT statement with the DISTINCT operator
A SELECT statement with a GROUP BY clause or ORDER BY clause
A SELECT statement that is combined with another SELECT statement with the UNION, INTERSECT, or MINUS set operator
The WHERE clause of a SELECT statement
DEFAULT value of a column in a CREATE TABLE or ALTER TABLE statement
The condition of a CHECK constraint
The subquery and SET operations rule above should answer your Question.
And for the reason for NULL, when pseudocolumn(eg. NEXTVAL) is used with a SET operation or any other rules mentioned above, the output is NULL, as Oracle couldnt extract them in effect with combining multiple selects.
Let us see the below query,
select rownum from dual
union all
select rownum from dual
the result is
ROWNUM
1
1
I would like to write a select where I show the value of the field as normal except when the field is null. If it is null I'd like to show a special text, for example "Field is null". How would I best do this?
// Oracle newbie
I like to use function COALESCE for this purpose. It returns the first non-null value from given arguments (so you can test more than one field at a time).
SELECT COALESCE(NULL, 'Special text') FROM DUAL
So this would also work:
SELECT COALESCE(
First_Nullable_Field,
Second_Nullable_Field,
Third_Nullable_Field,
'All fields are NULL'
) FROM YourTable
Just insert the NVL PL/SQL function into your query
SELECT NVL(SOMENULLABLEFIELD,'Field Is Null') SOMENULLABLEFIELD
FROM MYTABLE;
More detail here : http://www.techonthenet.com/oracle/functions/nvl.php
You could also use DECODE:
select value, decode(value, NULL, 'SPECIAL', value) from
(select NULL value from dual
union all
select 2 value from dual
)