Comparing Date with datetime format - sql

How do I shorten the following statement:
select * from orders
where to_char(trunc(Cancel_Date,'MONTH'),'dd/mm/yyyy')='01/03/2015'
and state = 'Cancelled'
and to_date(to_char(trunc(Order_Date,'MONTH'),'dd/mm/yyyy'),'dd/mm/yyyy') < to_date('01/03/2015','dd/mm/yyyy')

It looks like you're trying to retrieve orders that were ordered before March but cancelled in March? If so, I would avoid filters like this: to_char(trunc(Cancel_Date,'MONTH'),'dd/mm/yyyy')='01/03/2015'; if you have an index on cancel_date, then it won't be used (except on the off chance that you have a function-based index on that column!). I would do the following:
SELECT * FROM orders
WHERE status = 'Cancelled'
AND cancel_date >= DATE'2015-03-01'
AND cancel_date < DATE'2015-04-01'
AND order_date < DATE'2015-03-01';
In the above query I am using ANSI date literals (supported in Oracle since 9i, I believe) rather than using TO_CHAR(), TO_DATE(), etc. And I am not applying any functions to cancel_date or order_date so that the optimizer can use the indexes on those columns (if they exist).
On a side note using SELECT * rather than explicitly naming the columns you need is generally not considered a best practice.

in the second part of the where condition truncate isn't necessary:
select *
from orders
where to_char(trunc(Cancel_Date,'MONTH'),'dd/mm/yyyy')='01/03/2015'
and state = 'Cancelled'
and Order_Date < to_date('01/03/2015','dd/mm/yyyy')

The function trunc returns a date. You don't need to convert that to char and back to date again.
where trunc(Cancel_Date,'MONTH') = to_date('01/03/2015','dd/mm/yyyy')
and state = 'Cancelled'
and trunc(Order_Date,'MONTH') < to_date('01/03/2015','dd/mm/yyyy')

Related

Is there a work around to avoid using TRUNC(Date) in sql in order to get benefit from indexes?

I have a table BOOKING_DETAILS which DEFINITE_DATE is a column with 'DATE' type. (DB is Oracle 12 c). also, DEFINITE_DATE column is indexed. I want the following query to optimize in order to use the index created for DEFINITE_DATE.
How can I do that?
select *
from BOOKING_DETAILS
where trunc(DEFINITE_DATE) = trunc(TO_DATE('2018-10-26', 'YYYY-MM-DD'))
You may use a between condition in the WHERE clause:
select *
from BOOKING_DETAILS
where DEFINITE_DATE >= date '2018-10-26' and
DEFINITE_DATE < date '2018-10-27';
This would return all records where the definite date falls on 26-October-2018 proper. The above WHERE clause is sargable, because it allows Oracle to use an index on DEFINITE_DATE, should it exist.
If you don't want to create index on trunc(DEFINITE_DATE) and use existing query, you can
select *
from BOOKING_DETAILS
where DEFINITE_DATE >= TO_DATE('2018-10-26', 'YYYY-MM-DD')
and DEFINITE_DATE < TO_DATE('2018-10-26', 'YYYY-MM-DD') + INTERVAL '1' DAY

Is there a way to define a named constant/parameter in a single-statement SQL query?

Tried to do something like:
WITH
dates as (SELECT '2015-01-01' as start, '2016-01-01' as end)
SELECT * FROM my_table WHERE start_date >= dates.start AND end_date <= dates.end
But got the error message "Relation 'dates' does not exist" (in Vertica). Is there any proper way to define a constant/parameter. In real example the query contains multiple selects over a defined time range, hence I'd like to maintain the values constants/parameters in a single place to allow them to be reused in the nested subqueries.
IF possible, I'd like to refrain from DECLARE/SET-like statements, where a separate line is required.
I would still do what #GordonLinoff mentioned (I doubt it has much impact on the query plan), but in case you really don't want to or like the feature I will show at the end...
You mentioned you were going to use vsql. You can do variables there.
\set start '2015-01-01'
\set end '2016-01-01'
SELECT *
FROM my_table
WHERE start_date >= :start
AND end_date <= :end;
Which is I guess kind of nice because you can also do things like:
\set start ''`date "+%Y-%m-%d %H:%M:%S"`''
Or echo the result of any command into a variable that you can use as a variable in your sql statement. Note that this variable is quite literal and you need to include any punctuation, quoting, etc. It isn't just a value, it's more like a template.
You need to have dates in the FROM clause if you want it in the query. You can do this as:
WITH dates as (SELECT '2015-01-01' as start, '2016-01-01' as end)
SELECT t.*
FROM my_table t JOIN
dates d
ON t.start_date >= d.start AND t.end_date <= d.end;
Note: You can also do this with a CROSS JOIN. I often write queries as:
WITH params as (
SELECT '2015-01-01' as start, '2016-01-01' as end
)
SELECT t.*
FROM params CROSS JOIN
my_table t
WHERE t.start_date >= params.start AND t.end_date <= params.end;

SQL: How to use 'LIKE' with a date 'between'?

So, i´m trying to select rows between two dates.
In db, the dates also have time.
Therefor i need to use LIKE.
SQL
$query = "SELECT * FROM table WHERE date >= LIKE :selectedDateFrom AND <= LIKE :selectedDateTo";
$query_params = array(':selectedDateFrom' => $selectedDateFrom.="%", ':selectedDateTo' => $selectedDateTo.="%");
This one returns error!
How should it look like?
In db, the dates also have time.
Therefor i need to use LIKE.
No, you don't.
To select all date/times where the date component is between (from) and (to), inclusive, you can write it as
SELECT *
FROM table
WHERE date >= :selectedDateFrom
AND date < :selectedDateToPlusOne
(Note the < instead of <=, and set the second parameter to one day after the last day you want to include in your results.) This works even when the column includes times.
you can't use like with dates in SQL
SO use this:
$query = "SELECT * FROM table WHERE date >= :selectedDateFrom AND date <= :selectedDateTo";
You'd strip the time part from a datetime with DATE().
SELECT *
FROM mytable
WHERE date(mydate) >= :selectedDateFrom
AND date(mydate) <= :selectedDateTo;
Or with BETWEEN for better readability:
SELECT *
FROM mytable
WHERE date(mydate) BETWEEN :selectedDateFrom AND :selectedDateTo;

Oracle SQL convert date format from DD-Mon-YY to YYYYMM

I have a to compare dates in 2 tables but the problem is that one table has the date in DD-Mon-YY format and the other in YYYYMM format.
I need to make both of them YYYYMM for the comparison.
I need to create something like this:
SELECT * FROM offers
WHERE offer_date = (SELECT to_date(create_date, 'YYYYMM') FROM customers where id = '12345678')
AND offer_rate > 0
where create_date is something like 12-Mar-2006 and offer_date is something like 200605
Any ideas where I need to adapt this query??
As offer_date is an number, and is of lower accuracy than your real dates, this may work...
- Convert your real date to a string of format YYYYMM
- Conver that value to an INT
- Compare the result you your offer_date
SELECT
*
FROM
offers
WHERE
offer_date = (SELECT CAST(to_char(create_date, 'YYYYMM') AS INT) FROM customers where id = '12345678')
AND offer_rate > 0
Also, by doing all the manipulation on the create_date you only do the processing on one value.
Additionally, had you manipulated the offer_date you would not be able to utilise any index on that field, and so force SCANs instead of SEEKs.
Am I missing something? You can just convert offer_date in the comparison:
SELECT *
FROM offers
WHERE to_char(offer_date, 'YYYYMM') = (SELECT to_date(create_date, 'YYYYMM') FROM customers where id = '12345678') AND
offer_rate > 0

ignoring timestamp in WHERE clause when comparing to date

My table has records like these
23-MAY-11 11.40.39.000000 AM
The following query brings nothing
SELECT *
FROM my_table
WHERE tenant_pha = 'test'
AND create_date >= TO_DATE('05/10/2011','mm/dd/yyyy')
AND create_date <= TO_DATE('05/23/2011','mm/dd/yyyy')
However, the below query will bring data
SELECT *
FROM my_table
WHERE tenant_pha = 'test'
AND create_date >= TO_DATE('05/10/2011','mm/dd/yyyy')
AND create_date <= TO_DATE('05/24/2011','mm/dd/yyyy')
I think this is because create_date column is time stamp.
How can I change my query to bring the desired result ( I want to avoid doing functions on the left side columns because they will make the query long).
You are right about the timestamp. '05/23/2011' is the same as '05/23/2011 12:00 AM'.
To include the whole day I usually move my date up by a day. < '05/24/2011' will include all of 5/23.
or change to '05/23/2011 23:59:59'
You can use trunc() without problems, you only need to create a function based index.
If you create this index:
CREATE INDEX idx_trunc_date ON my_table (trunc(create_date));
then the following condition will make use of that index:
AND trunc(create_date) >= TO_DATE('05/10/2011','mm/dd/yyyy')