How many times is to_date executed in where clause? - sql

In the following query, is the to_date function executed multiple times or just once? This query is running long and I'm trying to find a way around it without asking for an index to be added.
select edi_stat_rsn_cd
, TSET_ID
, count(*) as count
from comt_po_msg
where TSET_ID in ('PSH','ORD','850','870')
and trunc(crt_ts) = to_date('03-06-2017','mm-dd-yyyy') --here
and LOC_TYP_CD in ('BOSS','STH')
group by edi_stat_rsn_cd, TSET_ID;

Try the below query. This will avoid the TRUNC() function for every row,
SELECT EDI_STAT_RSN_CD
, TSET_ID
, COUNT(*) AS COUNT
FROM COMT_PO_MSG
WHERE TSET_ID IN ('PSH','ORD','850','870')
--AND TRUNC(CRT_TS) = TO_DATE('03-06-2017','MM-DD-YYYY') --HERE
AND TO_CHAR(CRT_TS, 'MM-DD-YYYY') = '03-06-2017'
AND LOC_TYP_CD IN ('BOSS','STH')
GROUP BY EDI_STAT_RSN_CD
, TSET_ID;

A reasonable optimizer should only call deterministic functions once on constants. But why bother? Instead, use the date keyword to include a date constant:
where TSET_ID in ('PSH', 'ORD', '850', '870') and
trunc(crt_ts) = date '2017-03-06' and
LOC_TYP_CD in ('BOSS', 'STH')
This has the additional advantage of allowing standard date formats.
The use of trunc(crt_ts) can prevent the optimizer from using an index -- which is a much bigger consideration than calling to_date(). I would recommend:
where TSET_ID in ('PSH', 'ORD', '850', '870') and
crt_ts >= date '2017-03-06' and
crt_ts < '2017-03-07' and
LOC_TYP_CD in ('BOSS', 'STH')

As already commented, it should be treated static since you are using static value to_date('03-06-2017','mm-dd-yyyy') and your to_date() function doesn't depend on column value (or) not getting evaluated based on per record value

Related

Why do I get a conversion failed error when I use my attribute on my WHERE clause but it works when I don't?

Why is my query returning a result when I run the following query:
SELECT MAX(CAST(IssueDate as Date))
FROM (SELECT IssueDate
FROM [Transient].[Commissions_TIB]
WHERE ISDATE(issuedate) = 1
GROUP BY IssueDate) t
But when I use the 'IssueDate' attribute in my WHERE clause it fails to convert to a date from a string?
SELECT MAX(CAST(IssueDate as Date))
FROM (SELECT IssueDate
FROM [Transient].[Commissions_TIB]
WHERE ISDATE(issuedate) = 1
GROUP BY IssueDate) t
WHERE CAST(IssueDate as Date) <= CAST(GETDATE() as date)
Most likely, there are IssueDates in your table that are not valid. For some reason, SQL Server seems to decide to apply the outer predicate before the inner predicate. I suspect that it relates to a query planner optimization technique called predicate pushdown - and, here, this might be considered a bug...
In a nutshell: don't use isdate(). It is not safe anyway, as it relies on some complicated (and undisclosed) heuristic. Instead, use try_convert() or try_cast(), which check and convert at once.
Your whole query can be simplified as:
SELECT MAX(TRY_CAST(IssueDate as Date))
FROM [Transient].[Commissions_TIB]
WHERE TRY_CAST(IssueDate as Date) <= CAST(GETDATE() as date)
Because ISDATE() is a terrible function. ISDATE(20200101), for example returns 1, but both CONVERT(datetime, 20200101) and CONVERT(date,20200101) returns different errors.
If you need to check is something is valid for another data type, use TRY_CONVERT: WHERE TRY_CONVERT(date,issuedate) IS NOT NULL
Of course, the real problem is your design. Don't store dates in a data type that isn't a date and time data type. Fix your design and this problem can't happen.

How to SELECT between two dates in SQL Server

I need to give a select count in a table using the range of two dates, however my dates are in "yyyy-MM-dd" format, and when I execute the select SQL it returns the following error:
Converting a varchar data type to a datetime data type resulted in a value out of range
Here is the query I'm using:
SELECT COUNT(*) AS score
FROM requests
WHERE date_em BETWEEN '2019-04-01' AND '2019-04-30'
Table structure
date_em = DATETIME
Can someone help me?
My preferred method is:
SELECT COUNT(*) AS score
FROM requests
WHERE date_em >= '2019-04-01' AND
date_em < '2019-05-01'
This handles the time component and will make use of an index.
The following also works well in SQL Server:
SELECT COUNT(*) AS score
FROM requests
WHERE CONVERT(date, date_em) BETWEEN '2019-04-01' AND '2019-04-30'
In SQL Server, this will also use an index (if available). However, normally functions on columns preclude the use of an index, so I'm more hesitant about this approach.
have you try this
SELECT COUNT(*) AS score FROM requests WHERE date_em BETWEEN '2019-04-01 00:00' AND '2019-04-30 23:59'
add 00:00 and 23:59 to make it look like a DateTime.

speeding up records fetch time

I have a SQL sentence which takes around 44 Sec to fetch the data. how can I reduce the time ?
this is the sql
Select AVNR,replace(to_char((CAST(DBTM as timestamp)),'hh24:mi'),'00:00','24:00') as ptime,
Wert
From e_mw_60min_me
Where to_char((CAST ( DBTM as TIMESTAMP)), 'DD-MM-YYYY') = '13-03-1396' and
AVNR In (1141,1142,1144,10335,10336,12016,1146,1147,1149,1129,1130,1132,1134,1135,1137,5895,5896,5900,8906,8907,8909,8901,8902,8904,8940,8941,8943,8951,8952,8954,8972,8973,8975,8830,8831,8833,8835,8836,8838,1113,1982,1984,2314,2315,2317,3272,3273,3275,3267,3268,3270,3262,3263,3265,10231,10136,9066,8779,8780,8782,8774,8775,8777,8320,8321,8323,7696,7697,7699,10486,10487,10489,3329,3330,12018,3322,3328,10132,3320,3321,12017,3222,3223,3225,686,687,689,691,692,694,696,697,699,1,2,4,10527,10528,10529,4911,4912,4914,4917,4918,4920,5162,5163,5166,5157,5158,5160,5168,5169,5171,5449,5450,5452,10116,10117,10119,10120,10121,10123,2271,2272,2279,2266,2267,2269,2259,2260,2262,1292,1293,1295,5380,5381,5383,5374,5375,5377,10545,10546,10547,3281,10126,12031,3244,3245,12030,10983,10984,12033,10987,10989,12032,11073,11074,1125,11079,11080,3333,105,106,108,100,101,103,93,94,96,29,30,32,95,102,1197,124,123,126,12085,12086,12087,2520,2521,2523,2525,2526,2528,2515,2516,2518,2510,2511,2513,5444,5445,5447,2201,2202,12025,3336,3337,3339,3002,3003,12029,1643,1644,1646,1609,1610,1612,1596,1597,1599,9717,9718,9720,9722,9723,9725,2146,2147,2149,2141,2142,2144,2136,2137,2139,2131,2132,2134,2121,2122,2124,2126,2127,2129,2635,2636,2638,2641,2642,2644,7499,7500,7502,4610,4611,4613,4605,4606,4608,4600,4601,4603,9074,9075,9077,9079,9080,9082,9235,9236,9238,9240,9241,9243,9245,9246,9248,8468,8469,8471,5785,5786,5788,5790,5791,5793,5691,5692,5694,5685,5686,5688,8455,8456,8458,11312,11313,4588,7654,7655,7657,9376,9377,9379,9371,9372,9374,9382,9383,9385,5918,5919,5922,5934,5935,5938,5912,5913,5916,10963,7860,10964,135,136,138,10658,10659,10664,10660,10661,10662,5173,5458,5460);
//these AVNRs are completley Dynamic
the Table is like :
AVNR (Like ID Number)
DBTM( reocrds every time for every points)
Wert (the value)
I want to know any other sql command which makes it faster to fetch
If DBTM is a date you don't need to cast it to a timestamp; and if there is an index on that then you should not convert it to a string to compare with another string - leave it as a date and convert the fixed value to a date too, and compare with a range that covers the entire day:
select AVNR,
replace(to_char(DBTM,'hh24:mi'),'00:00','24:00') as ptime,
Wert
from e_mw_60min_me
where DBTM >= date '1396-03-13'
and DBTM < date '1396-03-14'
and AVNR In (1141, ...);
I've used date literals but you can use to_date() if you are starting from a variable string:
where DBTM >= to_date('13-03-1396', 'DD-MM-YYYY')
and DBTM < to_date('13-03-1396', 'DD-MM-YYYY') + 1
and AVNR In (1141, ...);
You should also consider creating a collection for all the AVNR values you are looking for and using member of instead if IN, or explode the collection and join to it. It depends where those values are coming from though.
You can add an index:
create index idx_e_mw_60min_me_2 on (to_char((CAST ( DBTM as TIMESTAMP)), 'DD-MM-YYYY'), AVNR)
This exactly matches the where clause so it should speed up the query.

Getting wrong month

SELECT TOP 1000 [pk_Id]
,[fk_resumeID]
,[fk_LoginID]
,[fk_CompanyId]
,Convert(nvarchar(11),ViewDate,105)
FROM [RecruitingDB].[Recruiting].[tbl_ViewResumeStatus]
where Convert(nvarchar(10),ViewDate,106)
between Convert(nvarchar(10),'17-10-2015',106) and Convert(nvarchar(10),'23-10-2015',106)
I am using this above sql for getting only Oct. Record but I am getting Sept. record.
Have you any idea why this is happening.
You are comparing the alphabetic values. You should avoid that, when dealing with numeric or date values.
Example 17-10-2015 would unintentionally be between 16-01-2015 and 18-01-2015
Use this instead:
SELECT TOP 1000 [pk_Id]
,[fk_resumeID]
,[fk_LoginID]
,[fk_CompanyId]
,Convert(char(10),ViewDate,105)
FROM [RecruitingDB].[Recruiting].[tbl_ViewResumeStatus]
WHERE
ViewDate > '2015-10-17' and
ViewDate < dateadd(d, 1, '2015-10-23')
If ViewDate is a date, you can replace the WHERE clause with this:
WHERE
ViewDate BETWEEN '2015-10-17' and '2015-10-23'
Well it will do if you convert your dates to strings. Your query is looking for anything alphabetically between'17-10-2015' and '23-10-2015'. So for example that would include '18-09-2015'.
Don't do date arithmetic using strings. If for some reason you have to, then use YYYY-MM-DD formats:
SELECT TOP 1000 [pk_Id], [fk_resumeID], [fk_LoginID], [fk_CompanyId],
Convert(nvarchar(11),ViewDate,105)
FROM [RecruitingDB].[Recruiting].[tbl_ViewResumeStatus]
WHERE ViewDate >= '2015-10-17' AND ViewDate < '2015-10-24';
Also, note the use of the comparisons. BETWEEN can be dangerous, because the column could have a time component. This method works regardless of whether or not the column has a time component. And, it will take advantage of an index on ViewDate.
The following is also acceptable in SQL Server:
SELECT TOP 1000 [pk_Id], [fk_resumeID], [fk_LoginID], [fk_CompanyId],
Convert(nvarchar(11),ViewDate,105)
FROM [RecruitingDB].[Recruiting].[tbl_ViewResumeStatus]
WHERE CAST(ViewDate as date) BETWEEN '2015-10-17' AND '2015-10-23';
The conversion to date remove the time component. And, the function call is sargable, meaning that the query can still use an index on ViewDate.

Comparing Date with datetime format

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')