I need to be able to find a row based on a number range, saved as a text field. For example, the field tuesday looks like 540-1020. I want to retrieve this row if I search for 900. So far I have,
SELECT string_to_array(tuesday, '-')
FROM coverage
WHERE 900 IN string_to_array(tuesday, '-')
Where string_to_array(tuesday, '-') prints out like {540,1020}. How can I convert it into a selectable integer range?
Use a range.
SELECT string_to_array(tuesday, '-')
FROM coverage
WHERE 900 <# int4range(split_part(tuesday, '-', 1)::int4, split_part(tuesday, '-', 2)::int4, '[]');
That last parameter [] signifies an inclusive range where '100-900' would match. You could also do an exclusive upper range like [) (note the right paren) where '100-900' would not match because the upper number is excluded from the set of matching numbers.
For better query speed as your table gets larger, you can add a GIST functional index.
CREATE INDEX tuesday_range_idx ON coverage
USING GIST (int4range(split_part(tuesday, '-', 1)::int4, split_part(tuesday, '-', 2)::int4, '[]'));
This is exposing some weaknesses in your data model. By having each day as a column, you'd have to create a separate functional index for each column. You're also having to parse text into an array every time you run this. Typically you'd want the data in the table to match how you access it, not its serialized form.
Instead of
CREATE TABLE coverage (
id serial PRIMARY KEY,
year smallint, -- tracking by week
week_num smallint, -- for example
sunday varchar,
monday varchar,
tuesday varchar,
wednesday varchar,
thursday varchar,
friday varchar,
saturday varchar
);
why not something like
CREATE TABLE coverage (
id serial PRIMARY KEY,
day date NOT NULL UNIQUE,
daily_data int4range NOT NULL
);
INSERT INTO coverage (day, daily_data)
VALUES ('2020-06-02', '[540,1020]');
Then your search looks like
SELECT daily_data
FROM coverage
WHERE extract(DOW FROM day) = 2 -- Tuesday (Sunday is 0, Saturday is 6)
AND 900 <# daily_data;
You can make indexes for the daily data ranges, by date (already a unique index in my example), functional indexes for the day of the week, month, year, etc. Much more flexible.
Or if you absolutely want an array back from your SELECT
SELECT ARRAY[lower(daily_data), upper(daily_data)]
FROM coverage
WHERE extract(DOW FROM day) = 2 -- Tuesday (Sunday is 0, Saturday is 6)
AND 900 <# daily_data;
Related
I am looking for a function in PostgreSQL which help me to generate recurring date after every 90 days from created date
for example: here is a demo table of mine.
id date name
1 "2020-09-08" "abc"
2 "2020-09-08" "xyz"
3 "2020-09-08" "def"
I need furure date like 2020-12-08, 2021-03-08, 2021-06-08, and so on
First it's important to note that, if you happen to have a date represented as text, then you can convert it to a date via:
SELECT TO_DATE('2017-01-03','YYYY-MM-DD');
So, if you happen to have a text as an input, then you will need to convert it to date. Next, you need to know that if you have a date, you can add days to it, like
SELECT CURRENT_DATE + INTERVAL '90 day';
Now, you need to understand that you can use dynamic variables, like:
select now() + interval '1 day' * 180;
Finally, you will need a temporary table to generate several values described as above. Read more here: How to return temp table result in postgresql function
Summary:
create a function
that generates a temporary table
where you insert as many records as you like
having the date shifted
and converting text to date if needed
You can create a function that returns a SETOF dates/timestamps. The below function takes 3 parameters: a timestamp, an interval, the num_of_periods desired. It returns num_of_periods + 1 timestamps, as it returns the original timestamp and the num_of_periods each the specified interval apart.
create or replace
function generate_periodic_time_intervals
( start_date timestamp
, period_length interval
, num_of_periods integer
, out gen_timestamp timestamp
)
returns setof timestamp
language sql
immutable strict
as $$
select (start_date + n * period_length)::timestamp
from generate_series(0,num_of_periods) gs(n)
$$;
For your particular case to timestamp/date as necessary. The same function would work for your case with the interval specified as '3 months' or of '90 days'. Just a note the interval specified can be any valid INTERVAL data type. See here. It also demonstrates the difference between 3 months and 90 days.
I have to add column to table.
The column value is calculated based on the column already present in the table.
I have to get time-stamp (column already present) and then group them into 5 min time-slots.
E.g: if the time is:
13:03/13:02 then it should go as 13:00;
13:53/13:52 then it should go as 13:50;
13:21 then should go as 13:20 and so on
PS: basically I have to get time stamp in epoch (UNIX time stamp)format [the table has values in epoch as well as in regular time stamp]
So what I'm seeing is that you have times and you need to round them down to the nearest 5 minute increment. Try this:
DECLARE #table TABLE (times TIME)
INSERT INTO #table
VALUES ('13:03'),
('13:02'),
('13:53'),
('13:52'),
('13:21');
SELECT times,
DATEADD(MINUTE,-DATEDIFF(MINUTE,0,times) % 5,times) five_minute_increments
FROM #table
Results:
times five_minute_increments
---------------- ----------------------
13:03:00.0000000 13:00:00.0000000
13:02:00.0000000 13:00:00.0000000
13:53:00.0000000 13:50:00.0000000
13:52:00.0000000 13:50:00.0000000
13:21:00.0000000 13:20:00.0000000
Epoch Version
DECLARE #epoch BIGINT;
--Epoch is the seconds since Jan 1,1970
SET #epoch = DATEDIFF(SECOND,'1970-01-01','2015-04-01 12:06:00.000');
SELECT CAST(DATEADD(SECOND,#epoch - (#epoch %300),'1970-01-01 00:00:00.000') AS TIME) AS epochTimes
Results:
12:05:00.0000000
create table epoch1(epoch int not null,
epoch_date as dateadd(s,epoch,'19700101'))
insert epoch1(epoch) values(1331070999)
insert epoch1(epoch) values(1331070956)
insert epoch1(epoch) values(1331071998)
insert epoch1(epoch) values(1331071999)
select DATEADD(MINUTE,-DATEDIFF(MINUTE,0,dateadd(MINUTE,epoch,'19700101')) % 5,dateadd(MINUTE,epoch,'19700101')) as human ,DATEDIFF(MINUTE, '1970-01-01 00:00:00', DATEADD(MINUTE,-DATEDIFF(MINUTE,0,dateadd(MINUTE,epoch,'19700101')) % 5,dateadd(MINUTE,epoch,'19700101'))) as timeinterval
from epoch1
I created a table with a datetime field "dt". Using COPY command to load data. The corresponding value for the field from the file is just the hour information, i.e., say, 14:50:00. So, the value being stored is 1900-01-01 14:50:00. I don't need the date part. How to do that.
Or may be an alternate datatype which can store only time.
Amazon Redshift supports only date(year month day) and timestamp(year month day hour minute second) format, and it doesn't support time(hour minute second) format of Postgresql.
In my idea, there are two ways to work around.
As #Damien_The_Unbeliever mentioned, ignore the date part of the timestamp format.
create table date_test(id int, timestamp timestamp);
insert into date_test2 values (1, '1900-01-01 14:50:00');
insert into date_test2 values (2, '1900-01-01 17:20:00');
select * from date_test2 where timestamp > '1900-01-01 14:50:00';
select * from date_test where date_test.timestamp > '1900-01-01 14:50:00';
id | timestamp
----+---------------------
2 | 1900-01-01 17:20:00
(1 row)
Use char or varchar type to store the time value.
create table date_test2(id int, timestamp char(8));
insert into date_test2 values (1, '14:50:00');
insert into date_test2 values (2, '17:20:00');
select * from date_test2 where timestamp > '14:50:00';
id | timestamp
----+-----------
2 | 17:20:00
(1 row)
The second solution looks easier, but it is worse performance as Redshift doc says. If you store a large amount of data, you should consider of the first one.
Here are the related links to the document about date/time column.
http://docs.aws.amazon.com/redshift/latest/dg/c_best-practices-timestamp-date-columns.html
http://docs.aws.amazon.com/redshift/latest/dg/r_Datetime_types.html
What's the easiest way to update a table that contains a DATETIME column on TSQL with RANDOM value between 2 dates?
I see various post related to that but their Random values are really sequential when you ORDER BY DATE after the update.
Assumptions
First assume that you have a database containing a table with a start datetime column and a end datetime column, which together define a datetime range:
CREATE DATABASE StackOverflow11387226;
GO
USE StackOverflow11387226;
GO
CREATE TABLE DateTimeRanges (
StartDateTime DATETIME NOT NULL,
EndDateTime DATETIME NOT NULL
);
GO
ALTER TABLE DateTimeRanges
ADD CONSTRAINT CK_PositiveRange CHECK (EndDateTime > StartDateTime);
And assume that the table contains some data:
INSERT INTO DateTimeRanges (
StartDateTime,
EndDateTime
)
VALUES
('2012-07-09 00:30', '2012-07-09 01:30'),
('2012-01-01 00:00', '2013-01-01 00:00'),
('1988-07-25 22:30', '2012-07-09 00:30');
GO
Method
The following SELECT statement returns the start datetime, the end datetime, and a pseudorandom datetime with minute precision greater than or equal to the start datetime and less than the second datetime:
SELECT
StartDateTime,
EndDateTime,
DATEADD(
MINUTE,
ABS(CHECKSUM(NEWID())) % DATEDIFF(MINUTE, StartDateTime, EndDateTime) + DATEDIFF(MINUTE, 0, StartDateTime),
0
) AS RandomDateTime
FROM DateTimeRanges;
Result
Because the NEWID() function is nondeterministic, this will return a different result set for every execution. Here is the result set I generated just now:
StartDateTime EndDateTime RandomDateTime
----------------------- ----------------------- -----------------------
2012-07-09 00:30:00.000 2012-07-09 01:30:00.000 2012-07-09 00:44:00.000
2012-01-01 00:00:00.000 2013-01-01 00:00:00.000 2012-09-08 20:41:00.000
1988-07-25 22:30:00.000 2012-07-09 00:30:00.000 1996-01-05 23:48:00.000
All the values in the column RandomDateTime lie between the values in columns StartDateTime and EndDateTime.
Explanation
This technique for generating random values is due to Jeff Moden. He wrote a great article on SQL Server Central about data generation. Read it for a more thorough explanation. Registration is required, but it's well worth it.
The idea is to generate a random offset from the start datetime, and add the offset to the start datetime to get a new datetime in between the start datetime and the end datetime.
The expression DATEDIFF(MINUTE, StartDateTime, EndDateTime) represents the total number of minutes between the start datetime and the end datetime. The offset must be less than or equal to this value.
The expression ABS(CHECKSUM(NEWID())) generates an independent random positive integer for every row. The expression can have any value from 0 to 2,147,483,647. This expression mod the first expression gives a valid offset in minutes.
The epxression DATEDIFF(MINUTE, 0, StartDateTime) represents the total number of minutes between the start datetime and a reference datetime of 0, which is shorthand for '1900-01-01 00:00:00.000'. The value of the reference datetime does not matter, but it matters that the same reference date is used in the whole expression. Add this to the offset to get the total number of minutes between the reference datetime.
The ecapsulating DATEADD function converts this to a datetime value by adding the number of minutes produced by the previous expression to the reference datetime.
You can use RAND for this:
select cast(cast(RAND()*100000 as int) as datetime)
from here
Sql-Fiddle looks quite good: http://sqlfiddle.com/#!3/b9e44/2/0
we've used sql server as our persisted data store for Quartz.net. I'd like to write some queries looking # the Time values. Specifically - Qrtz_Fired_Triggers.Fired_Time, Qrtz_Triggers.Next_fire_time, Prev_fire_time.
For the life of me, I can't find anything that says what this data is - ticks, milliseconds, microseconds, nanoseconds. I've guessed at a couple of things, but they've all proven wrong.
The best answer would include the math to convert the big int into a datetime and perhaps even a link(s) to the pages/documentation that I should have found - explaining the meaning of the data in those fields.
If you have specific instructions on using Quartz .Net libraries to view this information, that would be appreciated, but, I really have 2 goals - to understand the meaning of the date/time data being stored and to keep this in T-SQL. If I get the one, I can figure out T-SQL or out.
On the SQL side, you can convert from Quartz.NET BIGINT times to a DateTime in UTC time with:
SELECT CAST(NEXT_FIRE_TIME/864000000000.0 - 693595.0 AS DATETIME) FROM QRTZ_TRIGGERS
Numbers Explanation
Values stored in the column are the number of ticks from .NET DateTime.MinValue in UTC time. There are 10000 ticks per millisecond.
The 864000000000.0 represents the number of ticks in a single day. You can verify this with
SELECT DATEDIFF(ms,'19000101','19000102')*10000.0
Now, if we take March 13, 2013 at midnight, .NET returns 634987296000000000 as the number of ticks.
var ticks = new DateTime(2013, 3, 13).Ticks;
To get a floating point number where whole numbers represent days and decimal numbers represent time, we take the ticks and divide by the number of ticks per day (giving us 734939.0 in our example)
SELECT 634987296000000000/(DATEDIFF(ms,'19000101','19000102')*10000.0)
If we get put the date in SQL and convert to a float, we get a different number: 41344.0
SELECT CAST(CAST('March 13, 2013 0:00' AS DATETIME) AS FLOAT)
So, we need to generate a conversion factor for the .NET-to-SQL days. SQL minimum date is January 1, 1900 0:00, so the correction factor can be calculated by taking the number of ticks for that time (599266080000000000) and dividing by the ticks per day, giving us 693595.0
SELECT 599266080000000000/(DATEDIFF(ms,'19000101','19000102')*10000.0)
So, to calculate the DateTime of a Quartz.NET date:
take the value in the column
divide by the number of ticks per day
subtract out the correction factor
convert to a DATETIME
SELECT CAST([Column]/864000000000.0 - 693595.0 AS DATETIME)
The value stored in database is the DateTime.Ticks value. From MSDN:
A single tick represents one hundred
nanoseconds or one ten-millionth of a
second. There are 10,000 ticks in a
millisecond.
The value of this property represents
the number of 100-nanosecond intervals
that have elapsed since 12:00:00
midnight, January 1, 0001, which
represents DateTime.MinValue. It does
not include the number of ticks that
are attributable to leap seconds.
So, unless I missed something and am making this too complicated, I couldn't get the dateadd functions in Ms Sql Server 2008 to handle such large values and I kept getting overflow errors. The approach I took in Ms Sql Server was this:
a) find a date closer to now than 0001.01.01 & its ticks value
b) use a function to give me a DateTime value.
Notes:
* for my application - seconds was good enough.
* I've not tested this extensively, but so far, it has acted pretty well for me.
The function:
CREATE FUNCTION [dbo].[net_ticks_to_date_time]
(
#net_ticks BIGINT
)
RETURNS DATETIME
AS
BEGIN
DECLARE
#dt_2010_11_01 AS DATETIME = '2010-11-01'
, #bi_ticks_for_2010_11_01 AS BIGINT = 634241664000000000
, #bi_ticks_in_a_second AS BIGINT = 10000000
RETURN
(
DATEADD(SECOND , ( ( #net_ticks - #bi_ticks_for_2010_11_01 ) / #bi_ticks_in_a_second ) , #dt_2010_11_01)
);
END
GO
Here is how I came up with the # of ticks to some recent date:
DECLARE
#dt2_dot_net_min AS DATETIME2 = '01/01/0001'
, #dt2_first_date AS DATETIME2
, #dt2_next_date AS DATETIME2
, #bi_seconds_since_0101001 BIGINT = 0
SET #dt2_first_date = #dt2_dot_net_min;
SET #dt2_next_date = DATEADD ( DAY, 1, #dt2_first_date )
WHILE ( #dt2_first_date < '11/01/2010' )
BEGIN
SELECT #bi_seconds_since_0101001 = DATEDIFF(SECOND, #dt2_first_date, #dt2_next_date ) + #bi_seconds_since_0101001
PRINT 'seconds 01/01/0001 to ' + CONVERT ( VARCHAR, #dt2_next_date, 101) + ' = ' + CONVERT ( VARCHAR, CAST ( #bi_seconds_since_0101001 AS MONEY ), 1)
SET #dt2_first_date = DATEADD ( DAY, 1, #dt2_first_date );
SET #dt2_next_date = DATEADD ( DAY, 1, #dt2_first_date )
END