Choose between two different date ranges - sql

I have a check box, when checked, my date range flips causing me now to choose which date range to look at.
So, 4/15/14 thru 4/20/14 … when my check box is checked, this date range is now 4/10/14 thru 4/15/14.
In my SQL Select I need to chose, based on this check box, which date range.
This didn't work ??
Where ( ? Between Date_1 and Date_2 ) or ( ? Between Date_2 and Date_1 )
Nor did this work ??
Where ( ?
Case
When Ck_Bx Is Null
Then Date_2 and Date_1
Else Date_1 and Date_2
End
)
Here is the SQL THAT IS WORKING AND I AM TRYING TO MODIFY THE "WHERE CLAUSE"
ExecuteSQL ( "
Select ToDo_Name_Calc, ToDo_Name
From ToDo
WHERE ( ( ? Between ToDo_Alert_Date and ToDo_Date ) or ( ? Between ToDo_Date and ToDo_Alert_Date ) ) and ToDo_Ck_Bx Is Null
Order By ToDo_Alert_Date Asc " ; " - " ; "" ;
cDateOfFirstPortal +11 )
Any assistance I would be grateful.
Tom

between is really just a syntax shortcut and it is the exact equivalent of:
( field >= small-value and field <= larger-value )
Let's say we use 2014-04-20 as the larger-value, but the field contains time as well as date. So evaluating <= 2014-04-20 against a stored value of 2014-04-20 11:12:13 means that value is ignored. In truth we really do want that value included (it occurs DURING the day of 2014-04-20) and the most reliable way of protecting we don't make that mistake is to get all data that is less than 2014-04-21.
So, the greatest problem faced when using date ranges using between is that you could miss almost 24 hours of data if you get it wrong. A safer approach avoids this problem by using less than for the upper date - but you add one day to it, and because we need to add one day we may have to use database specific code (e.g. date_add() for MySQL, dateadd() for SQL Server/Sybase).
Not using between, which is the safer option, requires some date arithmetic, represent here simply by +1
SELECT
ToDo_Name_Calc
, ToDo_Name
FROM ToDo
WHERE (
? >= ToDo_Alert_Date AND ? < (ToDo_Date+1) --*
AND ToDo_Ck_Bx IS NULL)
OR (
? >= ToDo_Date AND ? < (ToDo_Alert_Date+1) --*
AND ToDo_Ck_Bx IS NOT NULL
)
ORDER BY
ToDo_Alert_Date ASC
--* use the relevant date addition method for your dbms.
Another reason for not using between here is that the dates MUST be in a specific order or nothing is returned', the older date must be first, the younger date must be last. You cannot just reverse them inside the between syntax.

Related

Hive query to return single row based on eff and exp date

I have a table with the following data.
I am expecting row which needs to be returned is with exp_dt "2020-09-22". But when run below query it returning both the rows. I am not understanding why it is returning the first row also when it has eff_dt "2020-09-19".
select id,cd,eff_dt,exp_dt,post_dt from table
where from_unixtime(unix_timestamp(eff_dt,"yyyy-MM-dd")) <= from_unixtime(unix_timestamp("2020-09-21","yyyy-MM-dd"))
and from_unixtime(unix_timestamp(exp_dt,"yyyy-MM-dd")) >= from_unixtime(unix_timestamp("2020-09-21","yyyy-MM-dd"));
Is there any issue with my query? I am expecting 2nd row to be returned.
Use < for the comparison to exp_date:
select id,cd,eff_dt,exp_dt,post_dt
from table
where from_unixtime(unix_timestamp('2020-09-21', 'yyyy-MM-dd')) >= from_unixtime(unix_timestamp(eff_dt, 'yyyy-MM-dd')) and
from_unixtime(unix_timestamp('2020-09-22', 'yyyy-MM-dd')) < from_unixtime(unix_timestamp(exp_dt, 'yyyy-MM-dd'))
I reversed the comparison order. I find it easier to follow the logic with the constants first.
Does this capture the edge case of equal same day expiry and solve your problem at the same time?
select id,cd,eff_dt,exp_dt,post_dt from table
where
(from_unixtime(unix_timestamp(eff_dt,"yyyy-MM-dd")) <= from_unixtime(unix_timestamp("2020-09-21","yyyy-MM-dd"))
and
from_unixtime(unix_timestamp(exp_dt,"yyyy-MM-dd")) > from_unixtime(unix_timestamp("2020-09-21","yyyy-MM-dd"))
)
or
(from_unixtime(unix_timestamp(eff_dt,"yyyy-MM-dd")) = from_unixtime(unix_timestamp("2020-09-21","yyyy-MM-dd"))
and
from_unixtime(unix_timestamp(exp_dt,"yyyy-MM-dd")) = from_unixtime(unix_timestamp("2020-09-21","yyyy-MM-dd"))
)
;
In fact I suspect exp is always >= eff, so maybe only one condition
from_unixtime(unix_timestamp(eff_dt,"yyyy-MM-dd")) <= from_unixtime(unix_timestamp("2020-09-21","yyyy-MM-dd"))
is enough...?
You do not need from_unixtime(unix_timestamp()) because dates are already in correct format and argument is in the same yyyy-MM-dd format.
The issue in your query is that you are using equal for both eff and exp dates
To find latest record on date use this query:
select id,cd,eff_dt,exp_dt,post_dt from table
where eff_dt <= "2020-09-21"
and exp_dt > "2020-09-21";
There should be no records when eff_dt = exp_dt in SCD2 if you have only date (without time component). dates can be equal only if you are using timestamps, and time is different, in this case convert your argument date to timestamp before checking.
SCD2 should be designed in such way that fact record can be mapped to exactly one record of SCD2.

Timestamp to date in SQL

Here is what I did:
Select count(check_id)
From Checks
Where timestamp::date > '2012-07-31'
Group by 1
Is it right to do it like I did or is there a better way? Should/could I have used the DateDIFF function in my WHERE clause? Something like: DATEDIFF(day, timestamp, '2012/07/31') > 0
Also, I need to figure out how I'd calculate the total rate of acceptance for this
time period? Can anyone provide their expertise with this?
Is it right to do it like I did or is there a better way?
Using a cast like that is a perfectly valid way to convert a timestamp to a date (I don't understand the reference to the non-existing datediff though - why would adding anything to a timestamp change it)
However, the cast has one drawback: if there is an index on the column "timestamp" it won't be used.
But as you just want a range after a certain date, there is no reason to cast the column to begin with.
The following will achieve the same thing as your query, but can make use of an index on the column "timestamp" in case there is one and using it is considered beneficial by the optimizer.
Select count(distinct check_id)
From Checks
Where "timestamp" > date '2012-07-31' + 1
Note the + 1 which selects the day after, otherwise the query would include rows that are on that date but after midnight.
I removed the unnecessary group by from your query.
If you want to get a count per day, then you will need to include the day in the SELECT list. In that case casting is a good way to do it:
Select "timestamp"::date, count(distinct check_id)
From Checks
Where "timestamp" > date '2012-07-31' + 1
group by "timestamp"::date

PostgreSQL: Query for tstzrange that contains last instant of a quarter

Given a PostgreSQL table that is supposed to contain rows with continuous, non-overlapping valid_range ranges such as:
CREATE TABLE tracking (
id INT PRIMARY KEY,
valid_range TSTZRANGE NOT NULL,
EXCLUDE USING gist (valid_range WITH &&)
);
INSERT INTO tracking (id, valid_range) VALUES
(1, '["2017-03-01 13:00", "2017-03-31 14:00")'),
(2, '["2017-03-31 14:00", "2017-04-01 00:00")'),
(3, '["2017-04-01 00:00",)');
That creates a table that contains:
id | valid_range
----+-----------------------------------------------------
1 | ["2017-03-01 13:00:00-07","2017-03-31 14:00:00-06")
2 | ["2017-03-31 14:00:00-06","2017-04-01 00:00:00-06")
3 | ["2017-04-01 00:00:00-06",)
I need to query for the row that was the valid row at the end of a given quarter, where I'm defining "at the end of a quarter" as "the instant in time right before the date changed to be the first day of the new quarter." In the above example, querying for the end of Q1 2017 (Q1 ends at the end of 2017-03-31, and Q2 begins 2017-04-01), I want my query to return only the row with ID 2.
What is the best way to express this condition in PostgreSQL?
SELECT * FROM tracking WHERE valid_range #> TIMESTAMPTZ '2017-03-31' is wrong because it returns the row that contains midnight on 2017-03-31, which is ID 1.
valid_range #> TIMESTAMPTZ '2017-04-01' is also wrong because it skips over the row that was actually valid right at the end of the quarter (ID 2) and instead returns the row with ID 3, which is the row that starts the new quarter.
I'm trying to avoid using something like ...ORDER BY valid_range DESC LIMIT 1 in the query.
Note that the end of the ranges must always be exclusive, I cannot change that.
The best answer I've come up with so far is
SELECT
*
FROM
tracking
WHERE
lower(valid_range) < '2017-04-01'
AND upper(valid_range) >= '2017-04-01'
This seems like the moral equivalent of saying "I want to reverse the inclusivity/exclusivity of the bounds on this TSTZRANGE column for this query" which makes me think I'm missing a better way of doing this. I wouldn't be surprised if it also negates the benefits of typical indexes on a range column.
You can use <# operator for check when value is within range:
SELECT *
FROM tracking
WHERE to_timestamp('2017-04-01','YYY-MM-DD')::TIMESTAMP WITH TIME ZONE <# valid_range;
Test PostgreSQL queries online

select * from table where datetime in month (without breaking index)

I have a bunch of timestamped rows (using the 'datetime' data type)
I want to select all the rows that have a timestamp that is within a particular month.
The column is indexed so I can't do MONTH(timestamp) = 3 because that'll make this index unuseable.
If I have year and month variables (in perl), is there a horrific bit of SQL I can use like:
timestamp BETWEEN DATE($year, $month, 0) AND DATE($year, $month, 31);
But nicer, and actually works?
I would actually go with the idea you proposed ; maybe with a small difference :
select *
from your_table
where date_field >= '2010-01-01'
and date_field < '2010-02-01'
(Of course, up to you the use $year and $month properly)
Note the < '2010-02-01' part : you might have to consider this, if you have dates that include the time.
For instance, if you have a line with a date like '2010-01-31 12:53:12', you probably want to have that line selected -- and, by default, '2010-01-31' means '2010-01-31 00:00:00'.
Maybe that doesn't look 'nice' to the eye ; but it'll work ; and use the index... It's the kind of solution I generaly use when I have that kind of problem.
This is substantively Pascal MARTIN's answer, but avoids having to know explicitly what the next year/month is (so you don't have to increment year and wrap around the $month, when $month == 12):
my $sth = $mysql_dbh->prepare(<<__EOSQL);
SELECT ...
FROM tbl
WHERE ts >= ? AND ts < (? + INTERVAL 1 MONTH)
__EOSQL
my $yyyymm = $year . '-' . sprintf('%02d', $month);
$sth->execute($yyyymm, $yyyymm);
For bonus fugly points, you could also do this:
... WHERE ts BETWEEN ? AND (? + INTERVAL 1 MONTH - INTERVAL 1 SECOND)
That - INTERVAL 1 SECOND will coerce the upper boundary from a DATE into a DATETIME/TIMESTAMP type set to the last second of a day, which is, as Pascal indicated, what you want on the upper bound.
If you need the same month of every year the index you have will not help you and no amount of SQL syntax trickery will help you
On the other hand if you need a month of a particular year then any query with date ranges should do it
Another alternative is to add an extra column to the table that stores the month, precomputed. That would just be a simple int column, and is trivial to index. Unless you're dealing with a kajillion rows, the extra space for an unsigned tiny int is neglible (one byte + db overhead per row).
It'd require a bit of extra work to keep synched with the timestamp column, but that's what triggers are for.
How about WHERE MONTH(`date`) = '$month' AND YEAR(`date`) = '$year'

Possible to use SQL to sort by date but put null dates at the back of the results set?

I have a bunch of tasks in a MySQL database, and one of the fields is "deadline date". Not every task has to have to a deadline date.
I'd like to use SQL to sort the tasks by deadline date, but put the ones without a deadline date in the back of the result set. As it is now, the null dates show up first, then the rest are sorted by deadline date earliest to latest.
Any ideas on how to do this with SQL alone? (I can do it with PHP if needed, but an SQL-only solution would be great.)
Thanks!
Here's a solution using only standard SQL, not ISNULL(). That function is not standard SQL, and may not work on other brands of RDBMS.
SELECT * FROM myTable
WHERE ...
ORDER BY CASE WHEN myDate IS NULL THEN 1 ELSE 0 END, myDate;
SELECT * FROM myTable
WHERE ...
ORDER BY ISNULL(myDate), myDate
SELECT foo, bar, due_date FROM tablename
ORDER BY CASE ISNULL(due_date, 0)
WHEN 0 THEN 1 ELSE 0 END, due_date
So you have 2 order by clauses. The first puts all non-nulls in front, then sorts by due date after that
The easiest way is using the minus operator with DESC.
SELECT * FROM request ORDER BY -date DESC
In MySQL, NULL values are considered lower in order than any non-NULL value, so sorting in ascending (ASC) order NULLs are listed first, and if descending (DESC) they are listed last.
When a - (minus) sign is added before the column name, NULL become -NULL.
Since -NULL == NULL, adding DESC make all the rows sort by date in ascending order followed by NULLs at last.