Inspired by the slected answer to Declare a variable in RedShift I am trying to use a query result as the format value in a to_char function call:
WITH tmp_variables as (
select 'YYYY-MM-DD' as date_format
)
SELECT to_char(OrderDate, (SELECT date_format FROM tmp_variables)) FROM Orders
But I am getting an error
TO_CHAR parameter: Second input must be a string literal
How can the tmp_variables's date_format value be used as a to_char format without getting an error or is there an alternative to using to_char where this would work?
SELECT is a SQL operator that work upon data. SQL is compiled before it can operate on data. The basic answer is that this won't work as written.
What you are trying to achieve isn't clear in the question - change date output format for some reason for some set of queries but not others? In the general case you will need to modify the SQL that goes to the compiler which will mean reading some configuration and merging this into the SQL text. If the use case is more limited there may be another way to achieve the desired result but only within some set of limitations.
Some possibilities - You could set a SQL variable with the format literal. Your client can read info and modify the query itself if it is capabile. A stored procedure could be used. A SQL modifier (pg_bouncer?) could live between the client and the cluster and substitute the string based on some other factors. Each of these has limitations and costs.
If you can describe the use case it could generate different / better ways.
Related
I am having trouble in my Oracle query that uses a variable stored in SSIS which has a date that is pulled from sql server.
I am using an execute sql task that simply gets a max date from a sql server table and stores it in a variable. E.g.
SELECT MAX(t.Date) FROM table t;
I then want to use that variable in my Oracle query which is an ADO.NET source connection. I noticed you can't parameterize in those connections and found the work around where you use the sql expression with your user variable in it. So now my Oracle source query looks something like this:
"SELECT DISTINCT t.* FROM table t WHERE TO_CHAR(t.LastUpdateDate, 'YYYY-MM-DD') > " + "'#[User::LastUpdateDate]'"
The query syntax itself is fine, but when I run it, it is pulling all rows and seems to be completely ignoring the where clause of the date.
I've tried removing the TO_CHAR from LastUpdateDate.
I've tried adding a TO_CHAR to my user variable #[User::LastUpdateDate].
I've tried using the CONVERSION() function from sql server on #[User::LastUpdateDate].
Nothing seems to work and the query just runs and pulls in all data as if I don't have the WHERE clause on the query.
Does anyone know how to rectify this issue or point out what I might be doing wrong?
Thank you for any and all help!
**EDIT:
My date being pulled from SQL Server is in this format: 2022-09-01 20:17:58.0000000
This is not an answer, just troubleshooting advice
You do not say what data type #[User::LastUpdateDate] is, I'll assume it's a datetime
Ideally all datetime data should be kept in datetime data types, then format becomes completely irrelevant. However since it's difficult to parameterise Oracle queries in SSIS, you have to concoct a string to be submitted. Now date format does become important.
On to something a little different, it is a very good habit performancewise, to not put functions around columns that you are searching on. This is called sargability - look it up.
Given these things, I suggest that you concoct your required SQL query bit by bit and troubleshoot.
First, format your date parameter as an Oracle date literal. Remember this is normally a bad and unecessary thing. We are only doing it because we have to concoct a SQL string.
So create another SSIS variable called strLastUpdateDate and put this hideous expression in it:
RIGHT("0" + (DT_STR,2,1252)DATEPART( "dd" , #[User::LastUpdateDate] ), 2) + '-' +
(DT_STR,3,1252)DATEPART( "mmm" , #[User::LastUpdateDate] ) + '-' +
(DT_STR,4,1252)DATEPART("yyyy" , #[User::LastUpdateDate] )
Yes this is ludicrously long code but it will turn your date variable into a Oracle string literal. You could simplify this by putting it into your original max query but lets not go there. Use whatever debugging technique you have to confirm that it works as expected.
Now you should be able to use this:
"SELECT t.*, '"+#[User::LastUpdateDate]+"' As MyStrDate FROM table t WHERE
t.LastUpdateDate > '" #[User::strLastUpdateDate] + "'"
You can try running that and see if it makes any difference. Make sure you use this https://dba.stackexchange.com/questions/8828/how-do-you-show-sql-executing-on-an-oracle-database to monitor what is actually being submitted to Oracle.
This is all from memory and googling - I haven't done SSIS for many years now
I suspect after all this you may still have the same problem because I recall from many years having the same mysterious issue.
The date format in Oracle SQL Developer looks like DD-MON-YY.
For example 17-OCT-03.
The date format in APEX looks like MM/DD/YYYY.
For example 12/17/1980.
Why are they different?
This might cause the same SQL query not to work on both applications.
I know that I can avoid such problem by using TO_DATE and TO_CHAR functions but I want to understand the logic behind this problem.
Does every application use its own default date format?
Yes, every application has it's own date format.
And even every application can have more than one session, each with a different session format specified for DATEs and TIMESTAMPs.
SQL Developer has it's application level settings defined in the Preferences, Database, NLS page. This is how DATEs will appear unless you issue an ALTER SESSION SET... in your SQL Worksheet.
Or, if you always want a specific format regardless of this setting, build it into your query.
select to_char(sysdate, 'DAY') today from dual;
It's been my experience as well... YES, every Oracle application/tool can have its own default date format, but most just use the default display format set at the database level:
SQL> select name, value from v$parameter where name = 'nls_date_format';
NAME VALUE
--------------------- ---------------------
nls_date_format DD-Mon-YY
1 row selected.
But I would not characterize this as "a problem"... it is a valuable feature that provides lots of flexibility in displaying and entering dates in Oracle-based applications.
For sure though, if developers don't understand how Oracle dates work and write code such as this:
-- BAD Coding
DECLARE
ld_holiday_date DATE;
BEGIN
-- Set a date type variable to a string value and
-- hope that Oracle can figure out what I mean.
ld_holiday_date := '01-JAN-2022';
END;
they are writing environment specific code which will definitely not work in another database (or session) that has a different NLS_DATE_FORMAT from the one they wrote their code in. Furthermore, the code above is having to do an implicit data type conversion (from string to date) which is leaving it up to Oracle to try and figure out what format is in the string.
To write more deterministic code with regard to Oracle dates, developers should definitely use the TO_DATE function. Here's the bullet proof version of the code snippet above:
-- Good Coding
DECLARE
ld_holiday_date DATE;
BEGIN
-- I do not care what the NLS date format is in this
-- database... this code will work everywhere.
ld_holiday_date := TO_DATE ('01-JAN-2022', 'DD-MON-YYYY');
END;
I have some sql statements, which i am using for Oracle. This sql statements are used by a programm from me.
I want to support Oracle and SQL-Server with my program without having different sql statements for Oracle and SQL-Server.
Which alternative can i use for the specific Oracle SQL-Statements:
to_char(FIELDNAME, 'YYYY')
to_char(FIELDNAME, 'YYYYMMDD')
to_char(FIELDNAME, 'DD.MM.YYYY')
The sql statements have to work for Oracle and SQL-Server.
Even if at a first glance the SQL implementation from two different vendors looks similar, when working with real life enterprise applications you will stumble upon a large number of differences, and I am only talking about SQL, when comparing PL/SQL with T-SQL there is hardly any resemblance left.
When trying to reduce the usage of two databases to only common functionality, you will loose a lot of their power, you could as well use a txt file on the file system.
One elegant solution, as someone already suggested, would be to leave the columns in the database as DATE data type and extract your data in the application code that stands above the database, if any. For example, in Java, you will map your database DATE columns to java.sql.Date no matter if that date comes from Oracle or from SQL Server.
Still, if you want to get your formatted data from the database, you could create separate columns that hold the formatted date, for example :
FIELDNAME | FIELDNAME_YYYY | FIELDNAME_YYYYMMDD | FIELDNAME_DDMMYYYY
I don't think there are common functions to do what you want. Oracle supports the ANSI standard extract() function for extracting date parts. SQL Server has separate functions for YEAR(), MONTH(), and DAY(). Oracle uses TO_CHAR(); SQL Server uses CONVERT().
One option is to define the functions YEAR(), MONTH(), and DAY() in Oracle and then use string concatenation (via the CONCAT()) function to combine the data. Or, write specific functions in each database for what you want to do. Or, perhaps, someone has implemented TO_CHAR() in SQL Server and you can grab the code from the web.
Finally i found a solution. Maybe its useful some other people too.
You can set the input format for a date...
Oracle: ALTER SESSION SET NLS_DATE_FORMAT = 'DD.MM.YYYY'
SQL-Server: SET DATEFORMAT dmy
How do you convert SQL mm/dd/yy datetime to mm/dd only? On Microsoft server.
Thanks all.
With dates and times it is an extremely common mistake to believe that what you see is what is stored. If the field is date, datetime, smalldatetime or datetime2 then what is stored are integers, not strings. So if the field is one of these, then:
convert(varchar(5),[date_field],1)
or
format([date_field],'MM/dd') -- mssql 2012 onward
If the information is a string already then left() will do the job.
Since you have specified an input format, the input must already be a string. Simply truncate with
cast(dateIn as char(5)).
You can use LEFT to just return the day and month:
SELECT LEFT('12/12/2000', 5)
I realize this isn't directly answering your question the way you asked it, but the best advice I can give is: Don't.
Instead, send back the field in its native datetime type. The database is not the place to be doing formatting. Instead, format the date in your application code.
For example, if you are calling SQL Server from a C#/.NET application, you could retrieve the value from a DataReader like this:
DateTime dt = (DateTime) reader["YourDateTime"];
Then you would format it as a string like this:
string s = dt.ToString("MM/dd");
This will ensure that the date is formatted correctly. If you are using a different language to call SQL Server, there are probably similar methods in that language.
One of the problems with the other approach mentioned (trunacating the string) is that the original value might not be formatted in mm/dd/yyyy to begin with. That all depends on the environment settings where the SQL Server is running. If you run the same code on an environment with dd/mm/yyyy settings, you would have unexpected results. This is avoided by using the native data type, the way I described.
I built a small query tool for Oracle using OracleCommand and OracleDataAdapter. Users just input a full query (no parameters), execute and the results are shown in a datagridview. So far so good, although I tried an invalid query, e.g.:
SELECT * FROM mytable WHERE dateColumn = '1-JAN-10'
This query is not valid SQL for Oracle. You have to use the to_date() function to compare with date literals. SQL developer also rejects it, but somehow my query tool just works. Does that mean my OracleCommand is a bit of a wizard here or am I doing something wrong? Also is there a way to omit this behavior because the purpose of the tool is testing queries, which should work always...
Thanks
The query may be valid for Oracle. You don't have to use to_date() if you give the date string in your session's date format, though it's generally better to do that anyway to avoid issues like this.
It sounds like you have a different NLS_DATE_FORMAT in your tool's environment to that in SQL Developer, or the session date format is being set implicitly by OracleCommand.
You can select value from nls_session_parameters where parameter = 'NLS_DATE_FORMAT' to see what it is from SQL*Plus and SQL Developer, and from your tool; and from nls_database_parameters to see which is overriding the database default.
Looks like your tool may have DD-MON-RR and you're expecting some other format elsewhere, but without checking those tables it's hard to say where you're using the database default and where you're overriding it at session level. I'd guess that is the DB default though and you have an override in your other environments.
From SQL Developer, try alter session set nls_date_format='DD-MON-RR'; and then re-run your invalid query - should work there too.