What is the purpose of using `timestamp(nullif('',''))` - sql

Folks
I am in the process of moving a decade old back-end from DB2 9.5 to Oracle 19c.
I frequently see in SQL queries and veiw definitions bizarre timestamp(nullif('','')) constructs used instead of a plain null.
What is the point of doing so? Why would anyone in their same mind would want to do so?
Disclaimer: my SQL skills are fairly mediocre. I might well miss something obvious.

It appears to create a NULL value with a TIMESTAMP data type.
The TIMESTAMP DB2 documentation states:
TIMESTAMP scalar function
The TIMESTAMP function returns a timestamp from a value or a pair of values.
TIMESTAMP(expression1, [expression2])
expression1 and expression2
The rules for the arguments depend on whether expression2 is specified and the data type of expression2.
If only one argument is specified it must be an expression that returns a value of one of the following built-in data types: a DATE, a TIMESTAMP, or a character string that is not a CLOB.
If you try to pass an untyped NULL to the TIMESTAMP function:
TIMESTAMP(NULL)
Then you get the error:
The invocation of routine "TIMESTAMP" is ambiguous. The argument in position "1" does not have a best fit.
To invoke the function, you need to pass one of the required DATE, TIMESTAMP or a non-CLOB string to the function which means that you need to coerce the NULL to have one of those types.
This could be:
TIMESTAMP(CAST(NULL AS VARCHAR(14)))
TIMESTAMP(NULLIF('',''))
Using NULLIF is more confusing but, if I have to try to make an excuse for using it, is slightly less to type than casting a NULL to a string.
The equivalent in Oracle would be:
CAST(NULL AS TIMESTAMP)
This also works in DB2 (and is even less to type).

It is not clear why - in any SQL dialect, no matter how old - one would use an argument like nullif('',''). Regardless of the result, that is a constant that can be calculated once and for all, and given as argument to timestamp(). Very likely, it should be null in any dialect and any version. So that should be the same as timestamp(null). The code you found suggests that whoever wrote it didn't know what they were doing.
One might need to write something like that - rather than a plain null - to get null of a specific data type. Even though "theoretical" SQL says null does not have a data type, you may need something like that, for example in a view, to define the data type of the column defined by an expression like that.
In Oracle you can use the cast() function, as MT0 demonstrated already - that is by far the most common and most elegant equivalent.
If you want something much closer in spirit to what you saw in that old code, to_timestamp(null) will have the same effect. No reason to write something more complicated for null given as argument, though - along the lines of that nullif() call.

Related

Is it possible to use a generic identifier in a SELECT query in SQLite?

I'm creating a function for querying a SQLite database that should be generic in the sense of reading from multiple pre-defined tables. As part of the function's paremeters, it is necessary to tel which column of the table should be read, an info that is supposed to be passed by an enumerator value. So the call would be something like this:
callMyFunction(enumTableA,enumColumnB);
Since enumColumnB is an enumerator value, the argument is an integer and I would like to identify the column by that integer value without having to use a switch-case for that. Something like this:
SELECT column(enumColumnB) from ...
So instead of passing the name of the column or reading all columns with * and then selecting the desired one when reading, it would use the number of the column as passed by the enumerator value to identify what should be read.
Is it possible to do this for SQLite? I couldn't find any tutorial mentioning such possibility, so I'm almost concluding there is no such possibility (and I'll have to pass the name of the column as argument or use a switch-case at the end), but I'ld like to be sure I don't have this option available.
SQLite has no mechanism for indirect columns.
(SQLite is designed as an embedded database, i.e., to be used together with a 'real' programming language.)
You have to replace the column name in whatever programming language you're using.

PROC SQL error: "ERROR: Expression using equals (=) has components that are of different data types."

I am trying to subset my data with PROC SQL, and it is giving me an error when I use my variable TNM_CLIN_STAGE_GROUP. Example below:
PROC SQL;
create table subset as
select ncdb.*
from ncdb
where YEAR_OF_DIAGNOSIS>2002
AND SEX = 2
AND LATERALITY IN (1,2,3)
AND HISTOLOGY = 8500
AND TNM_CLIN_STAGE_GROUP = 1;
quit;
ERROR: Expression using equals (=) has components that are of different data types.
When I run the same code, but take out the variable TNM_CLIN_STAGE_GROUP, the code works. Anyone know what the problem with that variable's name is?
That error indicates a difference in type. SAS has only two types, numeric and character, so the variable is probably character; verify the specific values, but in general it likely needs quotations (single or double, doesn't matter in this case).
If it is not a hardcoded value, but a value of another variable, you can use PUT to convert to character or INPUT to convert to numeric, whichever is easier to convert based on the data.
SAS in a data step will happily convert this for you, but in SQL and SQL-like (WHERE statements) it does not automatically convert character to numeric and vice versa; you must provide the correct type.
Before doing equality, check what you are trying to compare.
Check the structure of you ncbd table, in particulary field type of TNM_CLIN_STAGE_GROUP
You would see the real type, if its a varchar, you need to use single quote like #JChao suggest in is comment.
If its another type, so you need to adapt the comparator or use cast if you don t have choice.

PLSQL assignment and casting

Right, a bit of background first....I have a oracle package that has the following:
g_variable constant varchar(6):= pkg_sample.get_config_num('test');
The function above "get_config_num" retrieves the value as a integer value. This seems to work and return the correct value. However the value for 'test' retrieved is actually is a varchar, so it should not work. Running "pkg_sample.get_config_num('test')" against dual I get (null) but in the application it seems to display the correct value.
Am I correct to assume that because we have "constant varchar(6)" we are assigning its type (casting it) as a varchar so it is able to find it?
Evidently there are two implicit data type casts occurring -- one in the function and one in the assignment of the function's value to the constant.
I would be uncomfortable with this, and would prefer that a get_config_string procedure be used. A get_config_date would also be handy.

SQL Server - simple select and conversion between int and string

I have a simple select statement like this:
SELECT [dok__Dokument].[dok_Id],
[dok__Dokument].[dok_WartUsNetto],
[dok__Dokument].[dok_WartUsBrutto],
[dok__Dokument].[dok_WartTwNetto],
[dok__Dokument].[dok_WartTwBrutto],
[dok__Dokument].[dok_WartNetto],
[dok__Dokument].[dok_WartVat],
[dok__Dokument].[dok_WartBrutto],
[dok__Dokument].[dok_KwWartosc]
FROM [dok__Dokument]
WHERE [dok_NrPelnyOryg] = 2753
AND [dok_PlatnikId] = 174
AND [dok_OdbiorcaId] = 174
AND [dok_PlatnikAdreshId] = 625
AND [dok_OdbiorcaAdreshId] = 624
Column dok_NrPelnyOryg is of type varchar(30), and not null.
The table contained both integer and string values in this column and this select statement was fired millions of times.
However recently this started crashing with message:
Conversion failed when converting the varchar value 'garbi czerwiec B' to data type int.
Little explanation: the table contains multiple "document" records and the mentioned column contains document original number (which comes from multiple different sources).
I know I can fix this by adding '' around the the number, but I'm rather looking for an explanation why this used to work and while not changing anything now it crashes.
It's possible that a plan change (due to changed statistics, recompile etc) led to this data being evaluated earlier (full scan for example), or that this particular data was not in the table previously (maybe before this started happening, there wasn't bad data in there). If it is supposed to be a number, then make it a numeric column. If it needs to allow strings as well, then stop treating it like a number. If you properly parameterize your statements and always pass a varchar you shouldn't need to worry about whether the value is enclosed in single quotes.
All those equality comparison operations are subject to the Data Type Precedence rules of SQL Server:
When an operator combines two
expressions of different data types,
the rules for data type precedence
specify that the data type with the
lower precedence is converted to the
data type with the higher precedence.
Since character types have lower precedence than int types, the query is basically the same as:
SELECT ...
FROM [dok__Dokument]
WHERE cast([dok_NrPelnyOryg] as int) = 2753
...
This has two effects:
it makes all indexes on columns involved in the WHERE clause useless
it can cause conversion errors.
You're not the first to have this problem, in fact several CSS cases I faced had me eventually write an article about this: On SQL Server boolean operator short-circuit.
The correct solution to your problem is that if the field value is numeric then the column type should be numeric. since you say that the data come from a 3rd party application you cannot change, the best solution is to abandon the vendor of this application and pick one that knows what is doing. Short of that, you need to search for character types on character columns:
SELECT ...
FROM [dok__Dokument]
WHERE [dok_NrPelnyOryg] = '2753'
...
In .Net managed ADO.Net parlance this means you use a SqlCommand like follows:
SqlCommand cmd = new SqlCommand (#" SELECT ...
FROM [dok__Dokument]
WHERE [dok_NrPelnyOryg] = #nrPelnyOryg
... ");
cmd.Parameters.Add("#nrPelnyOryg", SqlDbType.Varchar).Value = "2754";
...
Just make sure you don't fall into he easy trap of passing in a NVARCHAR parameter (Unicode) for comparing with a VARCHAR column, since the same data type precendence rules quoted before will coerce the comparison to occur on the NVARCHAR type, thus rendering indexes, again, useless. the easiest way to fall for this trap is to use the dredded AddWithValue and pass in a string value.
Your query stopped working because someone inserted the text string in to the field you are querying using INT. Up until that time it was possible to implicitly convert the data but now that's no longer the case.
I'd go check your data and, more importantly, the model; as Aaron said do you need to allow strings in that field? If not, change the data type to prevent this happening in the future.

Should I tell NHibernate/FNH to explicitly use a string data type for a param mapped to a string column?

A cohort of mine is building a somewhat long search query based on various input from the user. We've got NHibernate mapped up using Fluent NHibernate, and aside from some noob mistakes, all seems to be going well.
One issue we can't resolve in the case of this search is that for a particular parameter, NHibernate is creating sql that treats the input as int when we explicitly need it to be a string. We have a string property mapped to an nvarchar(255) column which mostly contains integer numbers, excluding some arbitrary inputs like "temporary" or long numbers like 4444444444444444 which is beyond the int limit.
In the course of testing, I've seen a couple things: 1) If I prepend a 0 to the incoming value, NH generates the sql param as a string, appropriately so; 2) If the value can realistically be converted to an int, the resulting sql treats it as so. In case #2, if I run the generated sql directly through sql server, I get an exception when the query comes across an non-integer value (such as the examples I listed above). For some reason, when I just let NH do it's thing, I'm getting appropriate records back, but it doesn't make sense; I would expect it to fail or at least tell me that something is wrong with some records that can't be evaluated by SqlServer.
The mapping is simple, the data store is simple; I would be ok leaving well enough alone if I at least understood why/how NHibernate is making this work when running the same state manually fails... Any thoughts?
Are you running the exact same code directly into SQL Server?
NHibernate parameterises all of its queries, and will in doing so define what value is passed through to SQL in the parameters. Which is probably what you're asking about, the reason SQL my fail, is that by default it will only know the difference if you input:
select * from table_name
where col_name = 5
in comparison with
select * from table_name
where col_name = '5'
If you do not define it as a string with the 's it will search for an int, and try to convert all the varchar's to ints, which will obviously fail in some cases with strings.