How to use locale for strftime in BigQuery - google-bigquery

I am using BigQuery to output a formatted Timestamp value using STRFTIME_UTC_USEC function, the documentation leads me strftime C++ reference,
which specify modifiers like %b (for month) etc. which are locale specific,
is their a way to use locale specific month names using STRFTIME?
The only other alternative I see is to write my own UDF function and do a lookup using Map.

Even though STRFTIME_UTC_USEC function is based on C++'s strftime there is no provision to supply locale.
We usually recommend using Standard SQL which has FORMAT_TIMESTAMP function, but it does not allow changing locale either.
You probably don't have to write complex UDF, just a simple REPLACE or REGEXP_REPLACE can be enough. Or you can have an array with localized month names - ["Январь", "Февраль", "Март", "Апрель", ...] and get element out of it based on month EXTRACT(MONTH FROM date)

Related

What is the alternative of datefromparts in Presto (AWS Athena)

What is the best performance alternative of datefromparts SQL function in AWS Athena (Presto DB)?
The use case is:
I have the date parts (i.e. the day, month, and year) and need the date from these.
You would typically use parse_date(), with the proper format specifiers. If your date is in ISO format, you can directly use from_iso_date() (or from_iso_timestamp()).
On the other hand, if you need to extract dates part, you can use extract(), like:
extract(hour from current_timestamp)
Note that Presto also offers a full range of short function name that correspond to the possible extraction parts: year(), quarter(), month(), ...

Why does selecting PostgreSQL interval using Knex.js returns a JSON or JavaScript object rather than a string?

I have a PostgreSQL table which has a column of the type interval which is storing a time duration in the ISO 8601 format i.e. P1D equals "1 day".
The problem I am having is that when selecting this data from the database using Knex.js the data is converted from the string P1D into a JSON object {"days":1}, if I execute the same basic select query in the command line interface I get the string P1D back, and have the option to set the style of output SET intervalStyle = iso_8601.
As best I can tell this is being doing by a dependency of Knex.js called "node-pg-types" which in turn uses "postgres-interval". In Bookshelf.js you can set a data processor, and in using the "pg" module directly you can set different type behaviours, however it's not clear at all how to modify the behaviour of Knex.js in this regard, and yet Bookshelf.js can do this and is built on Knex.js.
In short my question is how do I make Knex.js output ISO 8601 style intervals on interval columns rather than a JSON object?
It turns out that through my research jumping from one module to another, that indeed Knex.js does use "node-pg-types" to format the interval columns, and that in turn is using "postgres-interval", neither module document this well at all.
In looking into "postgres-interval" it was evident that the data returned was a JavaScript object which was being encoded into what looked like JSON, however reading the documentation on this module it actually has functions you can call to get the data in any format:
https://github.com/bendrucker/postgres-interval
interval.toPostgres() -> string
Returns an interval string. This allows the interval object to be passed into prepared statements.
interval.toISO() -> string
Returns an ISO 8601 compliant string.
So the answer is to append .toISO() to your code.
I will notify the developer that this particular behaviour is not well documented so they can look to improve awareness of how Knex.js passes off some of the work to other modules which also pass work off, however I wrote this self answered question so no one else has to spend countless hours trying to figure this out.

Why would SQL statement return out of ordered dataset?

I have written a program that logs events into a db file as they happen using SQL statement. Whenever I open the table to view, I specifically request the dataset in descending order based on ALARM's date and time. It seems to work only for part of the table. I am using SQLite3 and my program is written in delphi or Pascal.
Here is the SQL statement:
SELECT *
FROM Alarms
ORDER BY datetime(ALARMTIME) DESC
Here is a snapshot of the table. Pay attention to the red arrows. That's where Alarm's date and time doesn't follow descending order. I don't know why this is happening.
I'm not sure how you created your date/time string in your database since that information wasn't given in the question. However, according to the documentation for the datetime() function, the format you have of MM/DD/YYYY HH:MM:SS xx is not one of the accepted formats. In fact, if you do SELECT datetime('1/23/2018 01:40:00 PM') at an SQLite prompt, you get NULL. Whereas, if you use an acceptable format, you do not: SELECT datetime('2018-01-23') gives '2018-01-23 00:00:00'.
So I think the solution is to write the ALARMTIME field using datetime('now'), which does yield a format accepted by datetime(). If you need to sort based upon the format you have now, you can't do it with datetime(). You'd need to reformat the field using string functions to get it in a form you could do a string compare with (e.g., as is done here).
The OP has indicated in a comment that the ALARMTIME is set using the following Pascal code:
FieldByName('AlarmTime').AsDateTime := now;
There's no guarantee that Pascal is going to use a date/time string format in this context that is compatible with SQLite's datetime() function. So Pascal date/time formatting functions can be used to create a format more specifically acceptable by SQLite's datetime(). Then you'd use something like:
FieldByName('AlarmTime').AsString := FormatDateTime('YYYY-MM-DD hh:nn:ss',now);
Now this will change the default view of the date to YYYY-MM-DD .... If you still want your view of the table to show MM/DD/YYYY... then you'll either need to go back to my prior comment about processing the string on the fly in the comparison for sort, or write a little view formatting code so that it displays in a format that's different than what is stored internally, which is a common view/model separation technique.
If you can write your original ALARMTIME format as MM/DD/YYYY and make sure you pre-pad with zeroes (e.g., 01/09/2018 not 1/9/2018) then you can use SQLite's substr function:
SELECT * FROM Alarms
ORDER BY (substr(ALARMTIME,7,4)||substr(ALARMTIME,1,2)||substr(ALARMTIME,4,2)||substr(ALARMTIME,11)) DESC
And you would create your ALARMTIME using this:
FieldByName('AlarmTime').AsString := FormatDateTime('dd-mm-yyyy hh:nn:ss', now);
The above solutions are fairly generic. Depending upon the client library you are using (which you have not specified), there may be another more suitable approach to solving the problem.
You cannot do, for example, SELECT * FROM Alarms ORDER BY ALARMTIME DESC and get an accurate sort since, for example, the date 12/1/2018 would come after 2/1/2018 in that sort ordering even though 2/1/2018 is later in time. This is because 2 follows 1 in the ASCII collating sequence.
If you need to keep your current ALARMTIME string format and not change how you're saving it, which is somewhat free form m/d/yyyy in which the day or month can have one or two digits, you're going to have a bit of work to do in order to sort it if your client library doesn't support some helpers in this regard. Perhaps your only other option would be to use a custom SQLite function. These are written in C and compiled and linked with SQLite. You'd have to find one already written, or write your own.

TO_CHAR and TO_DATE use and the right context to use them

If I have a query like:
select *
from CAT_ACCT_AUDIT_TRAIL cataccount0_
where cataccount0_.CAAT_EXECUTED_DATE >=TO_DATE(’26-AUG-2016′, ‘DD-MM-YYYY’) AND
to_Date(TO_CHAR(cataccount0_.CAAT_EXECUTED_DATE , ‘dd -mon-yyyy’), ‘DD-MM-YYYY’)<=TO_DATE('31-AUG-2016', 'DD-MM-YYYY')
Here why do we require the to_char or to_date functions? What is the right context to use them?
If I do either of these:
select TO_DATE('26-AUG-2016', 'DD-MM-YYYY') from dual;
select TO_DATE('01-12-2016', 'DD-MM-YYYY') from dual;
I get the output in NLS variable format as I set in the session, irrespective of date input in format conversion; I get the same result for both. Wy is this so?
What is the correct way to solve this query? I mean when i need to fetch the values in date range.
You use to_date() to convert a string like '01-12-2016' to the date datatype. You use to_char() to convert a date to a string. In both cases you specify the format of the string - if you don't then your session NLS settings are used, which is not good practice for anything except ad hoc queries as someone else running your code later may get a different output or an error.
A general rule - which your code is following - is to compare data of one type with values/constants of the same type. As your column is a date, you're supplying the filter values as dates - by converting strings to the date datatype. If you didn't do that then implicit conversion would happen, but you should not rely on that either as it can also lead to NLS issues, and depending on the type it can prevent indexes being used. Read more about data conversion in the documentation.
Oracle tries to be flexible when interpreting the string when you do to_date(). When you do TO_DATE('26-AUG-2016', 'DD-MM-YYYY') you are supplying the month as a string (in a specific language, which is another topic), but telling the function to expect a number. Oracle 'helpfully' interprets that anyway, so it usually works. But whatever format you use for to_date(), you aren't specifying the display format, so your client is deciding how to display the converted date as a string for you - usually using your NLS settings, again.
Doing this:
to_Date(TO_CHAR(cataccount0_.CAAT_EXECUTED_DATE , ‘dd -mon-yyyy’), ‘DD-MM-YYYY’)
is usually pointless, but even so should be using consistent format models. One reason this is sometimes done is if the source date (caat_executed_date here) has its time set to something other than midnight, and you want to discard the time. But there are better ways to do that - specifically the trunc() function, which by default sets the time to midnight.
When you have constant values, like TO_DATE('31-AUG-2016', 'DD-MM-YYYY'), you can also use ANSI date literals, in the form of DATE '2016-08-31'.
It is unclear what you want to do, but you don't actually need those functions on constants. Just use the date keyword for date literals. For instance:
where cataccount0_.CAAT_EXECUTED_DATE >= date '2016-08-26'
If you want to remove the time component from a date, then use trunc():
where trunc(cataccount0_.CAAT_EXECUTED_DATE, 'dd') -- the `'dd'` is optional for this purpose
This can be used in any context where a date constant is accepted.

Number of days between two dates - ANSI SQL

I need a way to determine the number of days between two dates in SQL.
Answer must be in ANSI SQL.
ANSI SQL-92 defines DATE - DATE as returning an INTERVAL type. You are supposed to be able to extract scalars from INTERVALS using the same method as extracting them from DATEs using – appropriately enough – the EXTRACT function (4.5.3).
<extract expression> operates on
a datetime or interval and returns an
exact numeric value representing the
value of one component of the datetime
or interval.
However, this is very poorly implemented in most databases. You're probably stuck using something database-specific. DATEDIFF is pretty well implemented across different platforms.
Here's the "real" way of doing it.
SELECT EXTRACT(DAY FROM DATE '2009-01-01' - DATE '2009-05-05') FROM DUAL;
Good luck!
I can't remember using a RDBMS that didn't support DATE1-DATE2 and SQL 92 seems to agree.
I believe the SQL-92 standard supports subtracting two dates with the '-' operator.
SQL 92 supports the following syntax:
t.date_1 - t.date_2
The EXTRACT function is also ANSI, but it isn't supported on SQL Server. Example:
ABS(EXTRACT(DAY FROM t.date_1) - EXTRACT(DAY FROM t.date_2)
Wrapping the calculation in an absolute value function ensures the value will come out as positive, even if a smaller date is the first date.
EXTRACT is supported on:
Oracle 9i+
MySQL
Postgres