Encode BC date in PostgreSQL - postgresql

Inspired by this question.
Is it possible to encode year, month and day (when year is negative) to the BC date in the simple way, without tricks?
Trying the direct way like
select make_date(-11,1,1);
ERROR: date field value out of range: -11-01-01
There are several alternatives like:
select make_date(1,1,1) - interval '11 years';
select format('%s-%s-%s BC', '0011','01','01')::date;
but obviously it is not the best approach.
So it seems like some kind of bug:
select extract(year from '0011-01-01 BC'::date);
╔═══════════╗
║ date_part ║
╠═══════════╣
║ -11 ║
╚═══════════╝
but using negative value of the year in the make_date function causing the error.
Tested on PostgreSQL 9.5

Yes, that seems like an oddity.
If you can come up with a patch for the pgsql-hackers mailing list, you've got chances to get it fixed.

Related

kdb - how to augment table with missing dates in a dynamic/fast way

I have an in-memory table with (date, sym, symType, factor, weight) as columns.
There are cases where this in-memory table once queried for a particular date range is missing an entire date. Could be today's data, or if we're querying for multiple dates, could be a day in the middle, or perhaps multiple days, or the last date, or the beginning.
How can I come up with with a query that fills in those missing dates with the max date up to that point?
So if we have data as follows:
Examples:
.z.D
.z.D-2
.z.D-3
.z.D-6
.z.D-7
I'd like the table to look like this:
.z.D -> .z.D
.z.D-1 -> copy of .z.D-2
.z.D-2 -> .z.D-2
.z.D-3 -> .z.D-3
.z.D-4 -> copy of .z.D-6
.z.D-5 -> copy .z.d-6
.z.D-6 -> .z.D-6
.z.D-7 -> .z.D-7
If in your query today is missing, use previous available date as today.
If in your query the last day is yesterday and it's missing, use the the previous available day as yesterday and so on.
if your last (min date) is missing, use the next available date upwards.
I can do this manually by identifying missing dates and going through missing dates day by day, but I'm wondering if there's a much better way to do this.
aj can work for dates in the middle by constructing a ([] date: listofdesireddates) cross ([] sym: listofsyms) cross ([] sectors: symtype) and then do an aj with the table but it doesn't solve all cases e.g if the missing day is today or at the start.
Can you come up with a reproducible example as to why aj doesn't work? Normal aj usage should solve this problem:
t1:([]date:.z.D-til 8;sym:`ABC);
t2:`date xasc([]date:.z.D-0 2 3 6 7;sym:`ABC;data:"I"$ssr[;".";""]each string .z.D-0 2 3 6 7);
q)aj[`sym`date;t1;t2]
date sym data
-----------------------
2020.07.20 ABC 20200720
2020.07.19 ABC 20200718
2020.07.18 ABC 20200718
2020.07.17 ABC 20200717
2020.07.16 ABC 20200714
2020.07.15 ABC 20200714
2020.07.14 ABC 20200714
2020.07.13 ABC 20200713
/If you need your last date to fill "upwards" then use fills:
update fills data by sym from aj[`sym`date;([]date:.z.D-til 9;sym:`ABC);t2]
A quick guess but a step function with xgroup on the result seems like it will work.
res:getFromTab[dates];
f:{`date xcols:update date:x from y#x};
xgrp:`s#`date xasc `date xgroup res;
raze f[;xgrp] each dates
Performance might be horrible ...

Q (KDB) selecting today's date within date range

I am trying to set up an dynamic threshold by different user, but only return result from today's date. I was able to return all the records from past 30 days, but I am having trouble only outputting today's date based on the calculation from past 30 days.. I am new to q language and really having a trouble with this simple statement :( (have tried and/or statement but not executing..) Thank you for all the help in advance!
select user, date, real*110 from table where date >= .z.D - 30, real> (3*(dev;real) fby user)+((avg;real) fby user)
Are you saying that you want to determine if any of todays "real" values are greater than 3 sigma based on the past 30 days? If so (without knowing much about your table structure) I'm guessing you could use something like this:
q)t:t,update user:`user2,real+(.0,39#10.0) from t:([] date:.z.D-til 40;user:`user1;real:20.1,10.0+39?.1 .0 -.1);
q)sigma:{avg[y]+x*dev y};
q)select from t where date>=.z.D-30, ({(.z.D=x`date)&x[`real]>sigma[3]exec real from x where date<>.z.D};([]date;real)) fby user
date user real
---------------------
2016.03.21 user1 20.1

Date Parameter to Cache SQL

I am very new to Cache. I am trying to develop a report with date parameters. When I issue the SQL command:
SELECT TOP 2 ad.admission_date from system.admission ad WHERE convert(sql_date,ad.admission_date) >= convert(sql_date,'08-01-2014' )
I get what I expect two records.
One of which is 10/1/2010 12:00:00 AM.
Then if I issue the command
SELECT TOP 2 ad.admission_date from system.admission ad WHERE convert(sql_date,ad.admission_date) <= convert(sql_date,'08-01-2014' )
I get no values returned?
When I issue the command
SELECT TOP 2 {fn convert('10-03-2010', sql_date) } FROM system.admission_data
I get two NULL values. Clearly I am confused about how Cache works.
I have found that if you use the standard ODBC format (yyyy-MM-dd) for the date you don't need to use the convert and it is much more efficient:
WHERE ad.admission_date <= '2014-08-01'
I formated date incorrectly. I have my code working now. Should look something like select top 2 convert(DATE, '10/03/2010 12:00:00 AM') .... and then I can actually do comparisons.

Call a function for each row in select - Postgres

I have a function called "getList(date)". This function returns me a items list (with several columns) from the date inputted in the parameter.
If I call:
SELECT * FROM getList('12/31/2014');
It works fine. It returns me a list with the date, the item name and the price.
Something like this:
date item_description price
-----------------------------------------------
12/31/2014 banana 1
12/31/2014 apple 2.5
12/31/2014 coconut 3
But I have another table with the dates that I want to search for.
So, I want to select all the dates from that table and, for each row returned, I want to call my function "getList" to have a result like this:
date item_description price
-----------------------------------------------
12/28/2014 banana 0.5
12/28/2014 apple 1.5
12/28/2014 coconut 2
12/31/2014 banana 1
12/31/2014 apple 2.5
12/31/2014 coconut 3
I don't know exactly how to do it. Of course my data is not a fruit list. This is just to explain the whole thing easier.
Thank you very much.
Correct way - LATERAL join
The correct way to do this is with a lateral query (PostgreSQL 9.3 or newer):
SELECT d."date", f.item_description, f.price
FROM mydates d,
LATERAL getList(d."date") f;
See the manual.
Legacy way - SRF in SELECT
In older versions you must use a PostgreSQL extension with some ... quirky ... properties, support for set-returning functions in the SELECT-list. Do not use this unless you know you must support PostgreSQL 9.2 or older.
SELECT d."date", (getList(d."date").*
FROM mydates d;
This may result in multiple-evaluation of the getList function, once for each column of the output.

Query only records that fall within a date range with postgresSQL (Redshift)

I am trying to grab all records from today's date to 14 weeks prior. I also need that same week but a year ago. I have the following:
WHERE date(date) >= date(dateadd(week,-14, current_date))
OR date(date) >= date(dateadd(week,-52, current_date))
OR date(date) <= date(dateadd(week,-53, current_date))
It doesn't seem to be working properly.
You could look into the use of INTERVAL to subtract from GETDATE() -- it may make things easier to read/reason about, but DATEADD should work in theory (although it isn't standard Postgres -- INTERVAL is).
One thing that immediately stands out from your stated requirements versus your code is that as written, from a boolean-logic perspective, the code doesn't match your requirements.
You said you need all records from between today and 14 weeks ago, and also from the same week a year ago.
What your code says is: give me all records where the date is within the past 14 weeks, or the date is greater than 52 weeks ago or the date is less than 53 weeks ago.
From a purely boolean perspective, what you want is more like something like this:
WHERE date(date) >= date(dateadd(week,-14, current_date))
OR (date(date) >= date(dateadd(week,-52, current_date))
AND date(date) <= date(dateadd(week,-53, current_date))
)
Note the additional set of parenthesis, and the switch amongst the additional set from or to and.
Additionally, I think you also may want to, within that additional set of parens, reverse the >= and <=, since in this case it's the larger negative number that represents the earlier date. This will all depend on exactly how DATEADD works, which I'm not familiar with since I use Postgres instead of Redshift (Redshift is a fork of Postgres 8.0), but based on how the Redshift doc seems to say it works, I believe you do want them reversed.
So then it becomes:
WHERE date(date) >= date(dateadd(week,-14, current_date))
OR (date(date) <= date(dateadd(week,-52, current_date))
AND date(date) >= date(dateadd(week,-53, current_date))
)
This is what I meant by it might be easier to reason about the logic when using INTERVAL -- using these large negative offsets, while they can certainly be made to work, just aren't as intuitive to reason about, IMO. It may also be worth switching the two sides of the inner AND, so the -53, the earlier value, is on the left side (won't change the functionality, but may make the use of DATEADD easier to reason about within this code).
Amazon Redshift is based on PostgreSQL 8.0. It doesn't support the "interval" data type, but docs say it does support interval arithmetic. I don't see any hint that it supports dateadd(), but I might have missed it.
This query will work as written on current versions of PostgreSQL and on SQLFiddle for testing and verification. I don't think it will work as written on Redshift, but the important part for you is just the WHERE clause. The common table expression is just to give us a calendar to do interval arithmetic against. You won't need it on Redshift.
It's not clear what you mean by "that same week but a year ago". You don't mention any particular week; you seem to be interested in a 14-week period. This query returns rows from
2014-09-23 (14 weeks ago) to 2014-12-30 (current date), and
the same dates in 2013.
with calendar as (
select (generate_series((current_date - interval '18 months'), current_date, '1 day'))::date cal_date
)
select cal_date
from calendar
where cal_date between (current_date - interval '14 weeks')::date
and current_date
or cal_date between ((current_date - interval '1 year')::date - interval '14 weeks')::date
and (current_date - interval '1 year')::date
order by cal_date;
cal_date
--
2013-09-23
...
2013-12-30
2014-09-23
...
2014-12-30