SQL Query variable vs hardcoded value - sql

I need explanation of something, as I couldn't find in on my own (probably because I don't know how to search for it).
I have a SQL Server query with some Common Table Expressions in it, one of those CTEs is selecting data based on date and user being not null e.g.
WHERE
"dummy"."UsageEnd" >= '20161001'
AND "dummy"."UsageEnd" < '20161101'
AND "Users"."Login" IS NOT NULL
In that form this query executes in ~2 seconds, but I need to change dates to a parameter, as this query will be executed very commonly. But if i change it to:
WHERE
"dummy"."UsageEnd" >= #start
AND "dummy"."UsageEnd" < #end
AND "Users"."Login" IS NOT NULL
Where #start and #end are declared as either datetime or varchar:
declare #datestart datetime
set #datestart = '20161001';
declare #dateend datetime
set #dateend = '20161101';
This subquery executes in 23-24 seconds, and whole query (as reminder this subquery is in CTE) is taking 7-8 minutes, when previously it was taking 12-15 seconds.
Can someone explain it to me why comparing dates to variables increased execution time so dramatically? Also is it possible, that whole query is taking so long because when in CTE is a variable it will re-evaluate it every time instead of just one?

The problem may well be caused by a combination of everyone's comments:
If your UsageEnd column is not a datetime datatype, but a varchar, the optimizer will first need to convert all values to a datetime type in order to make the comparison with the variables.
The first query with the "hardcoded constants" is already in varchar, so the optimizer is able to perform the comparison much faster.
Both plans will look different and give a clear indication of where to find the problem.

When an operator combines two expressions of different data types, the rules for data type precedence specify that the data type with the lower precedence is converted to the data type with the higher precedence. If the conversion is not a supported implicit conversion, an error is returned. MSDN

Related

Is comparing a date to datetime the same as first casting the former to a datetime in SQL Server?

I have a table with a column, [MyDate], of datatype DATE.
I have a variable #MyDateTime DATETIME.
Is the following comparison:
WHERE [MyDate] < #MyDateTime
semantically equivalent to:
WHERE CONVERT(DATETIME, [MyDate]) < #MyDateTime (in SQL Server 2012)? (Meaning the two comparisons will always produce the same results.) If not, what is a set of values for which the two are not the same?
This question is motivated by the fact that I observed that the former comparison allows me to more efficiently make use of an index on [MyDate] while the latter does not.
They are equivalent logically, but not functionally.
[MyDate] < #MyDateTime
is the same as
CONVERT(DATETIME, [MyDate]) < #MyDateTime
but I would write it as this
[MyDate] < CONVERT(DATE,#MyDateTime)
This would eliminate some of those edge cases where you are doing date vs datetime comparisons and forget about the hours and minutes; sadly, I've seen this multiple times during my career (thankfully not my mistakes).
Generally, if you wrap a column in a function as the function must be applied to all rows to determine the validity of the comparison, so the index can't be leveraged optimally. But as Gordon Linoff stated, the index is still be used based on my testing and is no slower when casting to a DATETIME on an indexed DATE column.

measuring the time of each operation in SQL query

I have a SQL query in the format of tree (A⨝B)⨝(C⨝D)⨝(E⨝F)⨝(G⨝H)⨝(I⨝J) containing different joins.I want to know that is there any method that we can find the time for each join operation separately like how much time sub expression (A⨝B) can take. Or (C⨝D) can take. instead of whole expression. Or how can we find the time for only the sub expression (A⨝B)⨝(C⨝D). I have converted my SQL query into tree by using Java language. Thanks in Advance. I am using SQL server for implementing my queries
I'm not sure if this is what you need but you could try with DATEDIFF if you can split each operation.
DECLARE #timer1 DATETIME
DECLARE #timer2 DATETIME
SET #timer1 = GETDATE()
--stuff to measure here
SET #timer2 = GETDATE()
SELECT DATEDIFF(millisecond,#timer1,#timer2 ) AS time_spent
I think you can compare different queries and see which one does the best.

Using GETDATE() to pull from a table

My apologies in advance for the basic (and probably silly) question.
I'm doing a SELECT query where I'm joining several tables. I have a table that contains a numeric value associated with certain days. What I would like to have in my output is the numeric value attached to today, but I'm clueless as to how to make this happen. Obviously, I would have the same value for every record in my output and I'm fine with that.
Is this even possible? If so, what would an example of the code be?
The table from which I would like the numeric value simply has dates in one column ([Calendar_Day]) and integers in another column ([X_Value]).
Below, you can see the last three lines of my SELECT statement. This latest attempt yielded NULL.
,[EnterpriseDB].[pcc].[conditions].Internal
,GETDATE() AS Date_Today
,(SELECT [X_Value] FROM [Analytics].[WorkArea].[Days_Vals] WHERE [Calendar_Day] = GETDATE()) AS BizVal_Today
Just guessing:
[Calendar_Day] is of the type date. While getdate() returns a datetime. That means the DBMS upcasts [Calendar_Day] to a datetime with a time of 00:00:00.0 in order to be able to compare the operands. But getdate() includes the current time. So unless you're executing the query at exactly 00:00:00.0 the values won't match and the subquery returns null.
To fix that, downcast getdate().
... WHERE [Calendar_Day] = convert(date, getdate()) ...
If [Calendar_Day] is also a datetime but you don't want to compare the hour (, minute, second ...) portion of it, downcast it as well.
... WHERE convert(date, [Calendar_Day]) = convert(date, getdate()) ...
Might throw any indexes out of the window though, if there are some on [Calendar_Day]. You might want to consider changing the data type to date or using a persistent, indexed computed column and compare against that, if that leads to any performance issues.

Strange behaviour of Sql query with between operator

There is this strange error in sql query.
The query is something like this.
select * from student where dob between '20150820' and '20150828'
But in the database the column of dob is varchar(14) and is in yyyyMMddhhmmss format,Say my data in the row is (20150827142545).If i fire the above query it should not retrive any rows as i have mentioned yyyyMMdd format in the query.But it retrives the row with yesterday date (i.e 20150827112535) and it cannot get the records with today's date (i.e 20150828144532)
Why is this happening??
Thanks for the help in advance
You can try like this:
select * from student
where convert(date,LEFT(dob,8)) between
convert(date'20150820') and convert(date,'20150828'))
Also as others have commented you need to store your date as Date instead of varchar to avoid such problems in future.
As already mentioned you would need to use the correct date type to have between behave properly.
select *
from student
where convert(date,LEFT(dob,8)) between '20150820' and '20150828'
Sidenote: You don't have to explicitly convert your two dates from text as this will be done implicitly as long as you use an unambiguous date representation, i.e. the ISO standard 'YYYYMMDD' or 'YYYY-MM-DD'. Of course if you're holding the values in variables then use date | datetime datatype
declare #startdate date
declare #enddate date
select *
from student
where convert(date,LEFT(dob,8)) between #startdate and #enddate
Sidenote 2: Performing the functions on your table dob column would prevent any indexes on that column from being used to their full potential in your execution plan and may result in slower execution, if you can, define the correct data type for the table dob column or use a persistent computed column or materialised view if your performance is a real issue.
Sidenote 3: If you need to maintain the time portion in your data i.e. date and time of birth, use the following to ensure all records are captured;
select *
from student
where
convert(date,LEFT(dob,8)) >= '20150820'
and convert(date,LEFT(dob,8)) < dateadd(d,1,'20150828')
All you have to do is to convert first the string to date.
select *
from student
where dob between convert(date, '20150820') and convert(date, '20150828')
Why is this happening?
The comparison is executed from left to right and the order of characters is determined by the codepage in use.
Sort Order
Sort order specifies the way that data values are sorted, affecting
the results of data comparison. The sorting of data is accomplished
through collations, and it can be optimized using indexes.
https://msdn.microsoft.com/en-us/library/ms143726.aspx
There are problems with between in T-SQL.
But if you want a fast answer convert to date first and use >= <= or even datediff to compare - maybe write a between function yourself if you want the easy use like between and no care about begin and start times ...
What do BETWEEN and the devil have in common?

Like operator in Datetime column

I want to use Like operator in a column of datetime. The values of the column is as follows:
2013-08-31 17:54:52.000
2013-08-31 17:54:52.000
My query is as below:
SELECT * FROM table where created_date Like '%54%'
It works fine. but when I search for '%52%' instead of '%54%', it gives me nothing. (It is working when I search till the minutes, but when I search for seconds or milli seconds it does not work.)
I have looked at the following url and it is working
SQL LIKE Statement on a DateTime Type
I want to know the reason, why this is happening and how like operator works with datetime type column.
I think it would be a better idea to use the DATEPART operator of SQL SERVER to extract the portion of date.
And example could be like:-
SELECT * FROM table
where DATEPART(minute,created_date)=54
EDIT:-
I want to know the reason, why this is happening and how like operator
works with datetime type column.
Actually there is no direct support given by SQL Server for LIKE operator for DATETIME variable but you can always cast the DATETIME to a VARCHAR and then try to use the LIKE operator as you want.
On a side note:-
MSDN says:-
DATEPART can be used in the select list, WHERE, HAVING, GROUP BY and
ORDER BY clauses.
In SQL Server 2012, DATEPART implicitly casts string literals as a
datetime2 type. This means that DATEPART does not support the format
YDM when the date is passed as a string. You must explicitly cast the
string to a datetime or smalldatetime type to use the YDM format.
The 'LIKE' operator and any regular expression operators provided by other databases are used to process text values. A date is definitely not a text value, it is a separata type by itself.
It makes little sense to apply a text operator to a non-text type (like int or DATETIME or DATETIMEOFFSET), which is why you can't use LIKE on dates in any database. First of all, the values are not stored as text but in an implementation-specific binary form.
Then, while you can use LIKE on a specific text representation of a date, eg using CAST you have to absolutely certain what that representation is. Different locales display dates differently, with year first, year last, month first or last or whatever. What would you search against?
Moreover, what is 54? 54 minutes, 54 seconds or 654 milliseconds?
The only sensible solution is to use DATEPART to check specific parts of a date, or the BETWEEN operator to check for ranges.