I need to get various parts of a TIMESTAMP field - specifically year, localized month name (in Russian), day of month and hours interval (like '11 - 12').
Currently i came up with this:
select
extract (year from prt.dtbegin) as f_year,
(
case extract (month from prt.dtbegin)
when 1 then 'Январь'
when 2 then 'Февраль'
/* ... */
when 12 then 'Декабрь'
end
) as f_month,
cast (lpad (extract (day from prt.dtbegin), 2, 0) as char(2)) as f_day,
(
cast (lpad (extract (hour from prt.dtbegin), 2, 0) as char(2))
|| ' - '
|| cast (lpad (extract (hour from prt.dtbegin) + 1, 2, 0) as char(2))
) as f_hour
from prt
It works fine (interval '23 - 24' is OK at the moment), but I don't like it, especially CASE sentence with each and every month.
So, i'd like to know, is there any common way of getting localized month names in Firebird? Also, can i format extracted parts of timestamp, instead of current cast-lpad-extract construct?
Thanks in advance.
What about a reference table with the localised strings in them with a key of the month number. I thought this was done in the Firebird system tables, but have not been able to find documentation or the location in the system tables, if indeed it is there.
EDIT:
I have checked the system tables, and the documentation, and the literals are not available within Firebird itself. Sorry :-)
Instead of CASE you can use DECODE builtin function: http://www.firebirdsql.org/refdocs/langrefupd21-intfunc-decode.html
I don't think there's a built-in feature for that. You should consider writing your own UDF for that. Once done, it's very simple to use. The following resources should point you in the right direction:
http://www.firebirdsql.org/index.php?op=useful&id=deatz_udf
http://www.firebirdfaq.org/faq83/
http://www.ibphoenix.com/downloads/writing_internal_udfs.pdf
I sure hope next major release (3.0) will support writing internal functions.
Update
Firebird 3.0 will support internal SQL Functions: http://tracker.firebirdsql.org/browse/CORE-2047
Related
I want to create a calculation that adds the dimensions current month and previous month to a Cognos Data Module. The Month format is 2022/11. This is what I tried. I do not get an error message, but the calculation does not return a result.
Case
when (Month_Adj = #timestampMask(_add_months($current_timestamp,0),'yyyy')+'/'+timestampMask(_add_months($current_timestamp,0),'mm')#) then 'Last Month'
when (Month_Adj = #timestampMask(_add_months($current_timestamp,-1),'yyyy')+'/'+timestampMask(_add_months($current_timestamp,-1),'mm')#) then 'Previous Month'
else null
end
Please find a screenshot for reference.
You are comparing a decimal result from your macro to a character value in your data. You don't have syntax errors because SQL implicitly casts the decimal value for the comparison. But the values are unlikely to match.
Using today's date, your macro code...
#timestampMask(_add_months($current_timestamp,0),'yyyy')+'/'+timestampMask(_add_months($current_timestamp,0),'mm')#
...should produce...
CAST(2022 AS DOUBLE PRECISION) / 11
So the resulting SQL is effectively...
Case
when (Month_Adj = 183.818181812) then 'Last Month'
when (Month_Adj = 202.2) then 'Previous Month'
else null
end
Use the sq() function to put quotes around the values returned by the macro functions before concatenating the three parts of the expression.
#sq(timestampMask(_add_months($current_timestamp, 0),'yyyy'))# + '/' + #sq(timestampMask(_add_months($current_timestamp, 0),'mm'))#
You can see the SQL that Cognos is producing by...
At the report level, More (elipsis) | Show generated SQL/MDX
In the query editor, Properties | DATA | Generated SQL
The Cognos relative time filters allow you to create a set of predefined relative time measures without too much mucking about.
If you want to have calculations referencing two or more relative time measures, either from the same fact or from different fact tables, you would need to create stand alone calculations and make sure that the calculate after aggregation flag is on.
I think the trickiest part is getting the lookup reference in the fact table set up.
Here are two expressions which could be used as patterns for your lookup references.
The first one is suitable for converting key values in the 202211 format into Year-month date data types.
cast (
substring(
cast( MONTH_KEY as varchar(8)) ,1,4 ) || '-'
+
substring(
cast( MONTH_KEY as varchar(8)) ,5,2 )||'-01'
, date)
The second one is suitable for converting key values in the 20221128 format into year-month-day date data types.
cast (
substring(
cast( SHIP_DAY_KEY as varchar(10)) ,1,4 ) || '-'
+
substring(
cast( SHIP_DAY_KEY as varchar(10)) ,5,2 )|| '-'
||
substring(
cast( SHIP_DAY_KEY as varchar(10)) ,7,2 )
, date)
If the key values are more abstract you would need to find a way to generate values which would map to those of the relative time filters.
Here is a link to an overview of relative time.
https://www.ibm.com/docs/en/cognos-analytics/11.2.0?topic=analysis-sample-calendars
Here's how to customize them:
https://www.ibm.com/docs/en/cognos-analytics/11.2.0?topic=analysis-creating-relative-date-filters
https://www.ibm.com/docs/en/cognos-analytics/11.2.0?topic=calendars-creating-custom-retail-calendar
https://pmsquare.com/analytics-blog/2020/1/10/creating-custom-calendars-in-cognos-analytics-111
https://senturus.com/blog/how-to-customize-relative-time-in-cognos-data-modules/
In 11.2.3 They have implemented where clause support for relative time, which makes queries with them a bit more efficient as you are not doing a bunch of local processing schlepping through the query results looking for the values which fit into the filters as the query will now be filtered to only have those results you want.
https://www.ibm.com/docs/en/cognos-analytics/11.2.0?topic=analysis-query-optimization-relative-date-measures
I have a requirement for Redshift SQL where I need to convert a column like '2020-Q2' to start of the quarter date 2020-04-01. I tried few other posts but they don't seem to work. Read somewhere I can use below query but it doesn't really work:
select to_timestamp(to_date('2015-Q1', 'YYYY-MM-DD'), 'HH24:MI:SS');
I am planning to make a UDF but if there is an in-built function already, please share. Thanks!
The following works in Postgres and I think it should work in Redshift:
select to_date(left(yyyyqq, 4), 'YYYY') + (right(yyyyqq, 1)::int - 1) * interval '3 month'
from (select '2015-Q3' as yyyyqq) x
I have been given some data where there is a 'float' column that contains a date with a 1 in the front of it. I.e the value 1171030 represents 30th October 2017.
I am stuck in attempting to convert it to a normal date, when attempting to remove the one a dot appears (i.e. .171030).
Would really appreciate some guidance.
Many thanks
Yuck! Let's try datefromparts():
select datefromparts((val - 100000) / 10000, floor(val / 100) % 100, val % 100)
Or, here is another approach:
select convert(date, right(convert(varchar(255), cast(val as int)), 6))
SQL Server is actually pretty flexible in the date formats it understands.
I'm assuming this was probably produced by a non-Y2K compliant method such as JavaScript getYear() which simply returns current year minus 1900. It's not clear to me whether this data is coming from a flat file or it is already imported into a database table.
If you treat this value as a character string:
case length(val)
when 7 then cast('20' + right(val, 6) as date)
when 6 then cast('19' + val as date)
end
If you truly have a float (or int) value:
cast(cast(cast(19000000 + #val as int) as char(8)) as date)
In this case you'll need to convert through a character value rather than going straight to date. Be careful with string conversions from float to char. If you prefer shorter then you may be able to get away with this one:
cast(str(19000000 + #val, 8) as date);
You might get errors by assuming a length of 8 but that's probably a good way to catch and prevent problems.
I am using Hive, so the SQL syntax might be slightly different. How do I get the data from the previous month? For example, if today is 2015-04-30, I need the data from March in this format 201503? Thanks!
select
employee_id, hours,
previous_month_date--YYYYMM,
from
employees
where
previous_month_date = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(),'yyyy-MM-dd') as int)
From experience, it's safer to use DATE_ADD(Today, -1-Day(Today)) to compute last-day-of-previous-month without having to worry about edge cases. From there you can do what you want e.g.
select
from_unixtime(unix_timestamp(), 'yyyy-MM-dd') as TODAY,
date_add(from_unixtime(unix_timestamp(), 'yyyy-MM-dd'), -1-cast(from_unixtime(unix_timestamp(), 'd') as int)) as LAST_DAY_PREV_MONTH,
substr(date_add(from_unixtime(unix_timestamp(), 'yyyy-MM-dd'), -1-cast(from_unixtime(unix_timestamp(), 'd') as int)), 1,7) as PREV_MONTH,
cast(substr(regexp_replace(date_add(from_unixtime(unix_timestamp(), 'yyyy-MM-dd'), -1-cast(from_unixtime(unix_timestamp(), 'd') as int)), '-',''), 1,6) as int) as PREV_MONTH_NUM
from WHATEVER limit 1
-- today last_day_prev_month prev_month prev_month_num
-- 2015-08-13 2015-07-30 2015-07 201507
See Hive documentation about date functions, string functions etc.
below works across year boundaries w/o complex calcs:
date_format(add_months(current_date, -1), 'yyyyMM') --previous month's yyyyMM
in general,
date_format(add_months(current_date, -n), 'yyyyMM') --previous n-th month's yyyyMM
use proper sign for needed direction (back/ahead)
You could do (year('2015-04-30')*100+month('2015-04-30'))-1 for the above mentioned date, it will return 201503 or something like (year(from_unixtime(unix_timestamp()))*100+month(from_unixtime(unix_timestamp())))-1 for today's previous month. Assuming your date column is in 'yyyy-mm-dd' format you can use the first example and substitute the date string with your table column name; for any other format the second example will do, add the column name in the unix_timestamp() operator.
Angelo's reply is a good start but it returns 201500 if the original date was 2015-01-XX. Building on his answer, I suggest using the following:
IF(month(${DATE}) = 1,
(year(${DATE})-1)*100 + 12,
year(${DATE})*100 + month(${DATE})-1
) as month_key
provided you get rid of those hyphens in your input string , previous date's month id in YYYYMM format you can get by:-
select if( ((${hiveconf:MonthId}-1)%100)=0 ,${hiveconf:MonthId}-89,${hiveconf:MonthId}-1 ) as PreviousMonthId;
I'm building a query against a DB2 database, connecting through the IBM Client Access ODBC driver. I want to pull fields that are less than 6 days old, based on the field 'a.ofbkddt'... the problem is that this field is not a date field, but rather a DECIMAL field, formatted as YYYYMMDD.
I was able to break down the decimal field by wrapping it in a call to char(), then using substr() to pull the year, month and day fields. I then formatted this as a date, and called the days() function, which gives a number that I can perform arithmetic on.
Here's an example of the query:
select
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM-
concat substr(char(a.ofbkddt),7,2) ) as difference, -- DD
a.ofbkddt as mydate
from QS36F.ASDF a
This yields the following:
difference mydate
2402 20050402
2025 20060306
...
4 20110917
3 20110918
2 20110919
1 20110920
This is what I expect to see... however when I use the same logic in the where clause of my query:
select
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM-
concat substr(char(a.ofbkddt),7,2) ) as difference, -- DD
a.ofbkddt as mydate
from QS36F.ASDF a
where
(
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM
concat substr(char(a.ofbkddt),7,2) ) -- DD
) < 6
I don't get any results back from my query, even though it's clear that I am getting date differences of as little as 1 day (obviously less than the 6 days that I'm requesting in the where clause).
My first thought was that the return type of days() might not be an integer, causing the comparison to fail... according to the documentation for days() found at http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/db2/rbafzmst02.htm, it returns a bigint. I cast the difference to integer, just to be safe, but this had no effect.
You're going about this backwards. Rather than using a function on every single value in the table (so you can compare it to the date), you should pre-compute the difference in the date. It's costing you resources to run the function on every row - you'd save a lot if you could just do it against CURRENT_DATE (it'd maybe save you even more if you could do it in your application code, but I realize this might not be possible). Your dates are in a sortable format, after all.
The query looks like so:
SELECT ofbkddt as myDate
FROM QS36F.ASDF
WHERE myDate > ((int(substr(char(current_date - 6 days, ISO), 1, 4)) * 10000) +
(int(substr(char(current_date - 6 days, ISO), 6, 2)) * 100) +
(int(substr(char(current_date - 6 days, ISO), 9, 2))))
Which, when run against your sample datatable, yields the following:
myDate
=============
20110917
20110918
20110919
20110920
You might also want to look into creating a calendar table, and add these dates as one of the columns.
What if you try a common table expression?
WITH A AS
(
select
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM-
concat substr(char(a.ofbkddt),7,2) ) as difference, -- DD
a.ofbkddt as mydate
from QS36F.ASDF a
)
SELECT
*
FROM
a
WHERE
difference < 6
Does your data have some nulls in a.ofbkddt? Maybe this is causing some funny behaviour in how db2 is evaluating the less than operation.