How can a substring from a varchar variable be null? - sql

Primarily as an SQL Server user I was suprised that in Oracle this syntax is valid:
select var
from table
where substr(var, 2, 1) is null
How can a subsrting from varchar string variable be null? In SQL Server this would never be true(?).
select var
from table
where substring(var, 2, 1) is null

In many cases in Oracle, the empty string is actually NULL.
If you substr() outside the range of your in-string, SQL server returns the empty string, while Oracle returns NULL (its equivalent to the empty string).

In general, null means absence of value.
select substr ('abcd',2, 1) from dual;
-- result: 'b'
select substr ('a',2, 1) from dual;
-- empty, because there is nothing at second position on string 'a';
select decode(substr('a',2,1),null, 'yes, is null') from dual;
-- result: 'yes, is null'
select decode('', null, 'yes, is null') from dual;
-- result: 'yes, is null'
Update:
It is clear a choice of implementation.
In most languages, for example in Java, Strings are placed in a memory area, and, for programmer, string variables are references to the area in the memory. So, an empty string is a reference to an area of length 0, but the reference isn't null. So, null is a different thing(no reference) and I think like you: it is better.

Related

Empty string being stored as null and need to differentiate between null and empty string in Orade [duplicate]

I am using Oracle DB. At the database level, when you set a column value to either NULL or '' (empty string), the fetched value is NULL in both cases. Is it possible to store '' (empty string) as a non NULL value in the database?
I execute this
UPDATE contacts SET last_name = '' WHERE id = '1001';
commit;
SELECT last_name, ID FROM contacts WHERE id ='1001';
LAST_NAME ID
------------ ------
null 1001
Is it possible to store the last_name as a non-NULL empty string ('')?
The only way to do this in oracle is with some kind of auxiliary flag field, that when set is supposed to represent the fact that the value should be an empty string.
As far as i know Oracle does not distinguish between '' and NULL, see here.
Oracle has a well know behavior that it silently converts "" to NULL on INSERT and UPDATE statements.
You have to deal with this in your code to prevent this behavior by converting NULL to "" when you read the columns back in and just do not use null in your program to begin with.
A long time since I used Oracle, but I believe we used to use a single space ' ' to represent an empty string, then trim it after reading.
If you use a VARCHAR2 data type then NULL and '' are identical and you cannot distinguish between them; so, as mentioned in other answers, you would either need to:
Have an additional column that contains a flag that distinguishes between non-NULL and NULL values so that if then flag states it is non-NULL and it contains a NULL then you know it is an empty string; or
Use an alternate representation, such as a single space character, for an empty string. This would then mean that you cannot store a string with that alternate representation; however, if trailing white-space was syntactically invalid for the strings you are storing then using a single space character to represent an empty string would be fine.
If you are using a CLOB data type then you CAN store an empty string using the EMPTY_CLOB() function:
CREATE TABLE table_name (value CLOB);
INSERT INTO table_name (value) VALUES (NULL);
INSERT INTO table_name (value) VALUES (EMPTY_CLOB());
INSERT INTO table_name (value) VALUES ('A');
Then:
SELECT value, LENGTH(value) FROM table_name;
Outputs:
VALUE
LENGTH(VALUE)
null
null
0
A
1
db<>fiddle here

SQL HASHBYTES function returns weird output when used in CASE WHEN/IIF

I have written a stored procedure that hashes the value of a certain column. I need to use this HASHBYTES function in a CASE WHEN or IIF statement, like this:
DECLARE #Hash varchar(255) = 'testvalue'
SELECT IIF(1=1, HASHBYTES('SHA1',#Hash), #Hash)
SELECT CASE WHEN 1=1 THEN HASHBYTES('SHA1',#Hash) END AS Hashcolumn
I can't get my head around why I get different outputs from above queries? it seems that whenever I add an ELSE in the CASE WHEN / IIF statement, it returns a string of weird characters (like ü<þ+OUL'RDOk{­\Ìø in above example).
Can anyone tell me why this is happening? I need to use the CASE WHEN or IIF.
Thanks guys
IIF returns the data type with the highest precedence from the types in true_value and false_value. In this case, it's #Hash1 which is varchar(255) so your result is getting cast to varchar(255). See below.
DECLARE #Hash varchar(255) = 'testvalue'
SELECT cast(HASHBYTES('SHA1',#Hash) as varchar(255))
Similarly, CASE works the same way. However, if you don't add an ELSE or another WHEN that would conflict with the data type, it will work. This is because an ELSE NULL is implied. i.e.
SELECT CASE WHEN 1=1 THEN HASHBYTES('SHA1',#Hash) END
However, if you add another check, then precedence kicks in, and it will be converted.
SELECT CASE WHEN 1=1 THEN HASHBYTES('SHA1',#Hash) WHEN 1=2 THEN #Hash END AS Hashcolumn
SELECT CASE WHEN 1=1 THEN HASHBYTES('SHA1',#Hash) ELSE #Hash END AS Hashcolumn
The output of a select query is a virtual table. In a relational db a column of a table is constrained to single data type.. so here what happens is implicit conversion is being done by the server engine inorder to render a sigle type and hence weird characters are returned.
The nature of conversion is as #scsimon says it follows highest precedence order.
The following query should help.
DECLARE #Hash varchar(255) = 'testvalue'
SELECT IIF(1=1, CONVERT(VARCHAR(255),HASHBYTES('SHA1',#Hash),2), #Hash)
SELECT CASE WHEN 1=2 THEN CONVERT(VARCHAR(255),HASHBYTES('SHA1',#Hash),2)
ELSE #Hash END AS Hashcolumn

Convert 'NULL' to Date in SQL

I have a column in my table called startdate. It is in string format. Most of the fields are 'NULL'. I am copying this column to another table which data type is 'Date'.
How can I convert all the values from string to Date in SQL.
I have tried this code:
INSERT INTO Destination_Table [new_date]
SELECT CONVERT(DATE,[startdate],103)
FROM Source_Table
nullif([startdate],'NULL') returns [startdate] unless it equals to 'NULL' and then it returns NULL (a real NULL, not the string 'NULL')
INSERT INTO Destination_Table [new_date]
SELECT CONVERT(DATE,nullif([startdate],'NULL'),103)
from Source_Table
For learning purposes, here are some expressions with the same results:
nullif(x,y)
case when x=y then null else x end
case x when y then null else x end
It looks like you are using MSSQL. If you are using MSSQL 2012, the following code should work :
INSERT INTO Destination_Table [new_date]
SELECT IIF([startdate] = "NULL", null, CONVERT(DATE,[startdate],103))
FROM Source_Table
What this does, is use the IIF() method to check the value of [startdate] and if the value is the text "NULL", then return the actual null value which can be allowed in most fields unless you have null disabled on the Destination_Table.[new_date] field.
Since the Date field can only accept and store Date/Time/Date&Time/(actual null) information, the text "NULL" is not valid.
Following is the equivalent for MySQL
INSERT INTO Destination_Table [new_date]
SELECT IF([startdate] == 'NULL', null, CONVERT(DATE,[startdate],103))
FROM Source_Table
(although I am unsure MySQL allows a conversion code as a param to CONVERT() )

How to do a conditional where clause with where in PL/SQL within Procedure

I have a pretty simple Stored Procedure that I am in trouble to do because i'm new to SQL and PL/SQL. I Have a table with a name column that is a varchar(55).
I discovered that if the user executes my procedure with an empty string as a paramter the LIKE statment brings all rows from TABLE1
SELECT *
FROM TABLE1
WHERE COLUMN LIKE VARIABLE || '%'
AND...
So I tried to change the query so if the VARIABLE is passed with a empty string it can still perform other conditions in the where statment.
SELECT *
FROM TABLE1
WHERE (VARIABLE <> '' AND COLUMN LIKE VARIABLE || '%')
AND...
But now wherever I pass as variable ('', NULL, 'anystring') I get no rows returned.
How can I build a query that validates if the variable is different of empty string and if it is it performs the LIKE statment with the variable correctly?
If I understand you correctly, it is not difficult thing to do. You can use conditional WHERE clause using CASE WHEN. So your query will support different scenarios, something like this:
SELECT *
FROM TABLE1
WHERE (CASE WHEN variable IS NULL AND column IS NULL THEN 1
WHEN variable LIKE '%' AND column LIKE variable||'%' THEN 1
ELSE 0
END) = 1
AND...
Basically, it checks if the variable = '' then it will compare the column against ''. Otherwise, it will compare it against variable||'%'.
Notice, Oracle treats empty string of the type VARCHAR as NULL (this does not apply to CHAR). So, in the first scenario we compare against NULL.
Hello Just a thought for this we can use Dynamic sql too. If you may try this approach. Hope it helps.
CREATE OR REPLACE PROCEDURE SPS_TEST_OUT(
p_input_in IN VARCHAR2
)
AS
lv_sql LONG;
lv_where VARCHAR2(100);
BEGIN
lv_where:= CASE WHEN p_input_in IS NULL OR p_input_in = '' THEN
''
ELSE
' AND COLUMN1 LIKE '''||p_input_in||'''%'
END;
lv_sql:='SELECT * FROM TABLE
WHERE 1 = 1
' ||lv_where;
dbms_output.put_line(lv_sql);
END;

Is it possible to store '' (empty string) as a non NULL value in the database?

I am using Oracle DB. At the database level, when you set a column value to either NULL or '' (empty string), the fetched value is NULL in both cases. Is it possible to store '' (empty string) as a non NULL value in the database?
I execute this
UPDATE contacts SET last_name = '' WHERE id = '1001';
commit;
SELECT last_name, ID FROM contacts WHERE id ='1001';
LAST_NAME ID
------------ ------
null 1001
Is it possible to store the last_name as a non-NULL empty string ('')?
The only way to do this in oracle is with some kind of auxiliary flag field, that when set is supposed to represent the fact that the value should be an empty string.
As far as i know Oracle does not distinguish between '' and NULL, see here.
Oracle has a well know behavior that it silently converts "" to NULL on INSERT and UPDATE statements.
You have to deal with this in your code to prevent this behavior by converting NULL to "" when you read the columns back in and just do not use null in your program to begin with.
A long time since I used Oracle, but I believe we used to use a single space ' ' to represent an empty string, then trim it after reading.
If you use a VARCHAR2 data type then NULL and '' are identical and you cannot distinguish between them; so, as mentioned in other answers, you would either need to:
Have an additional column that contains a flag that distinguishes between non-NULL and NULL values so that if then flag states it is non-NULL and it contains a NULL then you know it is an empty string; or
Use an alternate representation, such as a single space character, for an empty string. This would then mean that you cannot store a string with that alternate representation; however, if trailing white-space was syntactically invalid for the strings you are storing then using a single space character to represent an empty string would be fine.
If you are using a CLOB data type then you CAN store an empty string using the EMPTY_CLOB() function:
CREATE TABLE table_name (value CLOB);
INSERT INTO table_name (value) VALUES (NULL);
INSERT INTO table_name (value) VALUES (EMPTY_CLOB());
INSERT INTO table_name (value) VALUES ('A');
Then:
SELECT value, LENGTH(value) FROM table_name;
Outputs:
VALUE
LENGTH(VALUE)
null
null
0
A
1
db<>fiddle here