How to work with date and time in KDB - kdb

I tried to work with dateDtimespan type by subtracting one dateDtimespan from another, but KDB (QPad) always shows 0 as a result, why?
Also if I have, say, datetime 12.11.2014:22:33:00.000000000 in one column and only time 22:32:00.000000000 in another, how I may remove date part from the first column to subtract time portion from the second column?

to remove the date, you can use the cast operator, $. To reference only the time, you can prefix $ with `time as shown below.
q).z.z
2015.02.23T14:10:33.523
q)`time$.z.z
14:10:30.731
q)t:([]ts:10#.z.N;ti:.z.t-til 10)
q)exec `time$ts-ti from t
00:00:00.000 00:00:00.001 00:00:00.002 00:00:00.003 00:00:00.004 00:00:00.005..
You can see more examples here. http://code.kx.com/q/ref/casting/#cast

I'll prefer downcasting the timestamp to timespan first and then calculate the diff i.e. (`timespan$p)-n. No harm in using the other way (`timespan$p-n) but it is less explicit than the former.
q)dt:( [] p:2#2014.12.11D22:33:00.000000000;n:2#22:32:00.000000000)
q)select (`timespan$p)-n from dt
p
--------------------
0D00:01:00.000000000
0D00:01:00.000000000

Related

How to truncate date in postgres?

I am using below condition to truncate date in postgres
to_date(to_char(trunc(appointment_date),'YYYYMMDD')||appointment_end_time,''YYYYMMDDHH24:MI:SS')AS tq
How I can use this in postgres ?
Strange data typing, sometimes requires strange, looking at least, queries. Try (see fiddle)
date_trunc('day',appointment_date)
+ substr(appoinment_end,12)::interval
As your to_char() call uses the format 'HH24:MI:SS' for the "time" column, you can cast that column directly to a time value, e.g. using the :: operator: appointment_end_time::time.
To build a new timestamp from the date part of the appointment_date and the time value, just add them:
appointment_date::date + appointment_end_time::time
So first the timestamp is converted to a date (that does not have a time), and then the time value is added to that, which yields a timestamp.
Note that to_date() returns a date so your code would remove the just added time part again. You would need to use to_timestamp() if you really want a timestamp as the result.
To answer the question's title "how to truncate date in Postgres?" (which in reality refers to a timestamp not a date): you can either cast it to a date (see above) or you can use date_trunc() (not trunc()) with a unit to which it should be truncated. However, date_trunc returns a timestamp not a date value, so you couldn't add a time to the result.

how to use the age() function in postgresql

I have a column in the students table called birthdate. i need to find students over the age of 12.
select ......, age(timestamp 'birthdate') as StudentAge
from students
.....
where StudentAge > 11
I dont know if thats the proper syntax or if im using the correct function for the situation
I think most of your confusion comes from unfamiliarity with Postgres's rich type system, and the syntax it uses.
In the page on date/time functions, the age function is listed with two forms. Assuming you want to compare to "today", you want the form with a single argument:
Function: age(timestamp)
Return type: interval
Description: Subtract from current_date (at midnight)
Example: age(timestamp '1957-06-13')
Result: 43 years 8 mons 3 days
So, you have a function which takes a value of type timestamp, and returns a value of type interval.
The example shows the input being specified as timestamp '1957-06-13'; this is just a way of creating a value of type timestamp from a hard-coded value - like creating an object in an object-oriented language. In your query, birthdate is not a hard-coded value, it's the name of a column, so this is not the syntax you want. If the column is of type timestamp, you can just use age(birthdate) directly; if not, you might need to convert it, e.g. age(CAST(birthdate AS timestamp)).
The output is of type interval, not a number of years, so comparing it against 12 is unlikely to do what you want. Instead, you should compare it against another interval value. Similar to the timestamp '1957-06-13' example, you can write interval '12 years' to directly create an interval value representing 12 years.
So your comparison would look like age(birthdate) >= interval '12 years'.
I don't know that tutorial you are talking about, but the documentation has the following to say about column labels:
The entries in the select list can be assigned names for subsequent processing, such as for use in an ORDER BY clause or for display by the client application.
Observe the subsequent here: The SELECT list is (logically) processed after the WHERE clause, so you cannot use column labels there.
You'll have to repeat the expression. This is in accordance with the SQL standard.
Moreover, birthdate is not a string literal, so don't quote it. And remove the timestamp.

KDB Converting Subselect to Q Query

We have a Q query running on tick data which consolidates to OHLC on 1-minute bars.
select subsel:(
exec last datetime.date+1 xbar datetime.minute.z.Z
from `base
where instrument=`GBPUSD,
datetime=datetime.date+1 xbar datetime.minute.z.Z),
max(datetime),
min(datetime),
Open:first price,
High:max price,
Low:min price,
Close:last price,
Volume:count(i)
by DT:($)datetime.date+1 xbar datetime.minute.z.Z
from `base
where instrument=`GBPUSD,
datetime>=2017.07.03T10:20:00.00,
datetime<2017.07.03T10:20:59.999
The problem is the xbar date is synthetic on both the main table and the 'subselect', the exec "datetime=" needs to reference the main table and cannot find the alias approach to use. Considered an ej but as both sides are synthetic also could not find the construct.
There are several issues with your query before we even get to the subselect. First, datetime.minute.z.Z is invalid syntax. You probably don't need the .z.Z suffix there. Second, 1 xbar is redundant: 1 xbar x is x for integer x and datetime.minute is integer. You can just do datetime.date+datetime.minute to get datetimes rounded to minutes. (Note that if you use timestamps, as you should, rounding would simply be 0D00:01 xbar timestamp, for datetime, you would have to precompute a minute as U:reciprocal 24*60 and use that with xbar - U xbar timestamp.) Fourth, you cast xbar'd timestamps to strings in the by clause. If you really want them as strings - do it as a separate update after the aggregation. Finally, there are some minor issues such as redundant parentheses and ($) which in q should be spelled as string.
Now, back to the subselect. I think once you resolve the issues I highlighted above, you will find out that you don't need the subquery at all. The result will already have the xbar'd timestamps in the key column. If you want the result as a regular table - just use 0!.

MDX number of days between shell date dimension and regular date dimension

I have a shell date dimension, and a sale date dimension. Having trouble setting up a calculated measure with the difference in days between the 2 dates.
I have tried a number of things, and the calculation always seems to return an error.
mdx example is:
WITH
MEMBER [Measures].[TimeDate] AS [Date].[Day].currentmember
MEMBER [Measures].[DSODate] AS [DSO Date].[Day].currentmember
MEMBER [Measures].[DaysSinceSale] AS
DateDiff(
"d"
, [Measures].DSODate.MemberValue
, [Measures].TimeDate.MemberValue
)
Select
{[Measures].[DaysSinceSale]} ON COLUMNS,
{[Date].[Day].members} ON ROWS
from [Receivables];
I have tried using DateDiff, and tried just subtracting the 2 dates.
Assuming it may have something to do with the 2 date dimensions being of different hierarchies, but i am not really sure how to handle that.
MDX Results
Date conversions can be tricky in mdx so maybe initially try the following simple approach:
WITH
MEMBER [Measures].[TimeDate] AS [Date].[Day].currentmember
MEMBER [Measures].[DSODate] AS [DSO Date].[Day].currentmember
MEMBER [Measures].[DaysSinceSale] AS
DateDiff(
"d"
, VBA!CDate([Measures].DSODate.MemberValue)
, VBA!CDate([Measures].TimeDate.MemberValue)
)
Select
{[Measures].[DaysSinceSale]} ON COLUMNS,
{[Date].[Day].members} ON ROWS
from [Receivables];
Otherwise you might need to use the key and an approach similar to this:
MDX - Converting Member Value to CDate
I found a way to get this to work ...
Main issue was that i didn't have a crossjoin, like whytheq mentioned.
I also didn't need the custom Measures declared at the top.
The other adjustment i made was to utilize the DateKey in the date calculation. That seemed to work in all my tests, and improved performance quite a bit.
WITH
MEMBER [Measures].[DaysSinceSale] AS
[Date].[DateKey].CurrentMember.MemberValue - [DSO Date].[DateKey].CurrentMember.MemberValue
Select
{[Measures].[DaysSinceSale]} ON COLUMNS,
{[Date].[DateKey].Members * [DSO Date].[DateKey].members} ON ROWS
from [Receivables];
If you see any issues that may arise with using DateKey let me know. For what i am doing that seemed to pull back the correct date value, and allowed me to find the difference between dates without using a datediff function.

Filter data by different time intervals

I need to filter my query with different time intervals like that:
...
where
date >= '2011-07-01' and date <='2011-09-30'
and date >='2012-07-01' and date >='2012-09-30'
I suppose such code is not good, because these dates conflicts with each other. But how to filter only these two intervals, skipping everything else? Is it even possible? Because if I query like this, I don't get any results.I tried to use BETWEEN, but it does same thing.
I bypassed this by extracting quarters from years and calculating only third quarter. But then other quarters sum is showed as zero and I can't ignore these rows that have sum column with zero value. I tried to filter where price > 0 (column where sum goes), but it says that column do not exist. So I put it whole FROM under '('')' brackets to make it calculate sum before where clause, but it still does give me error that such column do not exist.
Also if you need to see query I have now, I can post it, just tell me if it is needed.
I want to do this, because I need to compare third quarter of two different years (maybe I should use another approach).
You're not going to get any results because you can't have a date that's both within 7/1/2011 through 9/30/11 and after 7/1/2012 and after 9/30/12.
You can have a date that is either between 7/1/20122 and 9/30/2011 or between 7/1/2012 and 9/30/2012.
SELECT col1 FROM table1
WHERE date BETWEEN '7/1/2011' AND '9/30/2011' OR date BETWEEN '7/1/2012' AND '9/30/2012';