How does the operator >+ differ from >= in SQL Server 2012 - sql-server

Entirely by accident today I was running a SQL statement to filter some items by date, for simplicity sake we'll say I used
SELECT *
FROM [TableName]
WHERE [RecordCreated] >+ '2016-04-10'
Only after the statement ran I realised I had used >+ instead of >=, now I was confused as I would have expected an error.
I tried a couple of other variations such as
>- -- Throws an error
<+ -- Ran successfully
<- -- Throws an error
The count of rows returned was exactly the same whether I used >= or >+
After searching online I couldn't find any documentation that covered this syntax directly, only when the two operators are used apart.
The RecordCreated column is a datetime.
Is this just a nicety in syntax for a possible common mistake or is it potentially trying to cast the date as a numeric value?

This seems to be a bug with '+' operator.
As per the updates from Microsoft team,
After some investigation, this behavior is by design since + is an
unary operator. So the parser accepts "+ , and the '+' is
simply ignored in this case. Changing this behavior has lot of
backward compatibility implications so we don't intend to change it &
the fix will introduce unnecessary changes for application code.
You can find a really good answer by RGO to his own question here.

The result shouldn't match with ">=" and "<=" but with ">" and "<". Just checked and the rowcound varies by 2 - the first and last item is removed.

Related

SQL Server : function precedence and short curcuiting in where clause

Consider this setup:
create table #test (val varchar(10))
insert into #test values ('20100101'), ('1')
Now if I run this query
select *
from #test
where ISDATE(val) = 1
and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00'
it will fail with
Conversion failed when converting date and/or time from character string
which tells me that the where conditions are not short-circuited and both functions are evaluated. OK.
However if I run
select *
from #test
where LEN(val) > 2
and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00'
it doesn't fail, which tells me that where clause is short-circuited in this case.
This
select *
from #test
where ISDATE(val) = 1
and CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00'
and LEN(val) > 2
fails again, but if I move length check to before cast, it work. So looks like the functions are evaluated in the order they appear in query.
Can anyone explain why first query fails?
It fails because SQL is declarative so the order of your conditions is not taken into account when the plan is generated (nor is it required to do so).
The usual way to get around this is to use CASE which has strict rules about sequence and when to stop.
In your case you will probably need nested CASEs, something like this:
WHERE
(
case when ISDATE(val) = 1 then
case when CAST(val as datetimeoffset) > '2005-03-01 00:00:00 +00:00' and
LEN(val) > 2
THEN 1 ELSE 0 END
ELSE 0
END
) = 1
(note this is unlikely to be actually correct SQL as I just typed it in).
By the way, even if you get it "working" by rearranging the conditions, I'd advise you don't. Accept that SQL simply doesn't work in that way. As the data changes & stats change, SQL is upgraded, workload varies, indexes are added the query plan could change. Any attempts to "get it working" are going to be short-lived at best so go with the CASE which will continue to work once you've got it right (provided you nest CASE statements where required and don't fall into the same precedence trap in the CASE conditions!)
The mystery is answered if you examine the Execution Plan. Both the CAST() and the LEN() are applied as part of the Table Scan step, while the test for IsDate() is a separate Filter test after the Table Scan.
It appears that the SQL Engine's internal optimizations use certain filtering functions as part of the retrieval of the data, and others as separate filters, almost certainly as a form of query optimization to minimize the load from disk into main memory. However, more complex functions, such as IsDate(), which is dependent on system variables such as system date format in some cases (is '01/02/2017' Jan 2nd or Feb 1st?), need to have the data retrieved before the filter is applied.
Although I have no hard information on this, I strongly suspect that any filter more resource intensive than a certain level is delegated to the Filter steps in the query plan, and anything simple/fast enough to be checked as the data is being read in is applied during the Scan/Seek steps. Also, if a filter could be applied on the data in the index, I am certain that it will be tested before any non-index data is tested, solely to minimize disk reads, which are bad performance juju (this may not apply on the Clustered index of the table). In these cases, the short-circuiting might not be straightforward, with an IsDate() test specified on a non-index field being executed after a similar test on an indexed field, no matter where they are in the list of conditions.
That said, it appears to be true that conditions short-circuit when they are executed in the same step of the query plan. If you insert a string like '201612123' into the temp table, then add a check on Len(val) < 9 after the date comparison, it still generates an error, instead of checking both LEN() conditions at the same time in a tiny optimization.
which tells me that where conditions are not short-circuited and both functions are evaluated.
To expand on LoztInSpace's answer, your terminology suggests you are not interpreting SQL correctly, on its own terms.
The various parts of a SELECT statement are not "functions". The entire statement is atomic. You supply the query as unit, and the DBMS responds. There is no "before" and no "after". There is just the query.
Those are the rules. Your job in formulating the query is to supply one that is valid. It's a logical progression: valid question, valid answer, etc. The moment you step out of that frame, you might as well be asking, "why is the sky seven?".
One a small clarification to #LoztInSpace's answer. When he refers to the order of your statements, he's presumably talking about the phrasing of your query, which for purposes of evaluation is inconsequential. Sequential SQL statements are executed sequentially, as presented. That is guaranteed by the SQL standard.

Anyone recognize this Syntax or Programming Language?

I am the local admin for an obsucure CRM-ATS prior to migrating to SFDC in 18 mos. It has a (basically BETA) report builder that is not well documented, but, appears very powerful. I have the capabilities to build custom expressions within the report, but, I can't figure the syntax for all the operators.
Does anyone recognize this code or operators list that might be able to point me to the proper language for syntax and creation of these individual expressions.
IF(GREATER_OR_EQUAL(DATE_DIFF(NOW(); JobCurrentStep.StepTime); 14); SUBSTRING("14Days+"; 1); SUBSTRING("<14 Days"; 1))
DATE_DIFF(StepsLinkedPeop.StepStartTime; StepsLinkedPeop.StepEndTime)
COUNT_DISTINCT(People.Person)
IF(LIKE(LinkedJobs.JobClientNameSBD; "MSP"); COUNT_DISTINCT(People.Person); 9)
COUNT_DISTINCT(People.Person)
IF(GREATER_OR_EQUAL(DATE_DIFF(StepChangesJour.StepStartTime; NOW()); 30); COUNT_DISTINCT(People.Person); 0
COUNT_DISTINCT(LinkedPeople.Applicant)
COUNT(LinkedPeople.Applicant)
DATE_DIFF(StepChangesJour.StepEndTime; StepChangesJour.StepStartTime)
GREATER_OR_EQUAL(DATE_DIFF(StepChangesJour.StepEndTime; StepChangesJour.StepStartTime); 7)
DATE_DIFF(NOW(); JobCurrentStep.StepTime)
IF(GREATER_OR_EQUAL(DATE_DIFF(NOW(); JobCurrentStep.StepTime); 500); SUBSTRING("Greater than 2 Weeks"; 1); SUBSTRING("Recent"; 1))
Here are the available operators:
AVG
CONCAT
COUNT
COUNT_DISTINCT
DATE_ADD_DAYS
DATE_ADD_SECONDS
DATE_DIFF
DATE_DIFF_IN_SECONDS
DATE_DIFF_IN_YEARS
DATE_FORMAT
DIVISION
EQUALS
GREATER
GREATER_OR_EQUAL
GROUP_CONCAT
GROUP_CONCAT_DISTINCT
GROUP_CONCAT_DISTINCT_WITH_HYPHEN
GROUP_CONCAT_DISTINCT_WITH_PIPES
HOUR_DIFF
IF
IF_NULL
IN
INET_NTOA
LIKE
LITERAL_NULL
LOCATE
LOGGED_USER_ID
LOGGED_USER_PERSON_ID
LOGGED_USER_TIMEZONE
MAX
MIN
MINUS
MULTIPLY
NOW
PCT
PLUS
REPLACE
ROUND
SUBSTRING
SUBSTRING_INDEX
SUM
SUM_DISTINCT
TO_DATETIME
TO_INT
TRIM
TRUNCATE
WORKING_DAYS
Though I don't assert this because I don't have experience with the language, this looks like ABAP to me, the high-level language created by the German software company SAP for its business applications.
Many of the operators you've mentioned look like MySQL function names or keywords. Notable examples include:
COUNT_DISTINCT, which looks a lot like COUNT(DISTINCT)
GROUP_CONCAT
INET_NTOA
LIKE
SUBSTRING_INDEX
However, many of the functions you've identified do not appear in MySQL; in particular, basic operators like PLUS, MULTIPLY and EQUALS are not functions in MySQL, and LOGGED_USER_ID and WORKING_DAYS do not appear in MySQL either. Additionally, the function call syntax you've described doesn't match what MySQL uses.
If I had to guess, I'd say you're looking at something custom that "compiles" expressions into MySQL queries.

Use of pipe symbol in Select clause

Is there any way/use of putting pipe symbol || in select clause.
I have come across following query in one of the article(probably to concatenate two values), but when I try to use the same in my query I am getting syntax error.
select FirstName ||''|| LastName As CustomerName from Customer
Please correct if I am using wrong syntax.
You can use CONCAT() function, which works in SQL Server 2012 and above, or just a plain + sign to do concatenation.
https://msdn.microsoft.com/en-us/library/hh231515(v=sql.110).aspx
Returns a string that is the result of concatenating two or more
string values.
you need to use '+' to perform Concat() instead of pipe if you are using SQL-Server. Pipe operator is not used in SQL-Server
It is used to concatenate you columns and output a single result i.e in one column.
For example, if i want to see first name and last name together as in one column then i could use pipes:
SELECT Fname||Lname FROM my_table;
If you are asking whether you can use pipes || for concatenation in Microsoft SQL, then the short answer is no.
If you’re asking about the concatenation operator itself, then read on.
|| is the standard ANSI concatenation operator. This is apparent in PostgreSQL, SQLite and Oracle, among others.
Microsoft, however uses +, because, why not. Except Microsoft Access uses &, because, why not.
MariaDB/MySQL have two modes. In traditional mode, || is interpreted as “or”, and there is no concatenation operator. In ANSI mode, || is interpreted as the concatenation operator.
Most DBMS (not SQLite) have the non-standard concat() function which will also concatenate. They also coalesce any NULLs to empty strings, so they’re a bit more forgiving if you don’t care about NULLs.

Why do I get a SQL syntax error with this?

Trying to run this query in LINQPad 4:
SELECT item_group_id as AccountID, IIF(ISNULL(t_item_group.description),'[blank]',t_item_group.description) AS Name
FROM t_item_group
WHERE active = TRUE
I get, "the isnull function requires 2 argument(s)."
I've tried moving the parens around, changing the "[blank]" to "[blank]" and "[blank]" , but none of it helps...
The queries (I have two similar ones (with IIF(ISNULL)) that LINQPad won't run for this reason, yet they run in actuality (in my Web API app) fine; so, LINQPad is more "picky" than it needs to be, perhaps, but what is it expecting, SQL syntax-wise?
ISNULL is already like a 'if' type statement.
You can just replace
IIF(ISNULL(t_item_group.description),'[blank]',t_item_group.description)
with
ISNULL(t_item_group.description, '[blank]')
The ISNULL uses the first parameter (the 'description'), unless that value is null in which case it will use the second parameter.
As an aside, one of the reasons I don't care for ISNULL is that it is poorly named. You'd assume that given its name it will return a bit - true if the parameter is null, false if not null - which you could use in an 'if' statement like you attempted. But that's not how it works.
The alternative is to use COALESCE. It provides much the same functionality, but the naming makes sense.
co·a·lesce ˌkōəˈles verb
1. come together and form one mass or whole.
To COALESCE two parameters is to force them into one non-nullable result. And the function is actually more powerful, as you can provide multiple parameters - COALESCE(i.description, i.name, '[blank]') is perfectly valid.

Meaning of ; in queries

What is the meaning of ; at the end of queries?
For example; what is the difference between:
select 1
select 2
and
select 1;
select 2
"From a SQLServerCentral.Com article by Ken Powers:
The Semicolon
The semicolon character is a statement terminator. It is a part of the ANSI SQL-92 standard, but was never used within Transact-SQL. Indeed, it was possible to code T-SQL for years without ever encountering a semicolon.
Usage
There are two situations in which you must use the semicolon. The first situation is where you use a Common Table Expression (CTE), and the CTE is not the first statement in the batch. The second is where you issue a Service Broker statement and the Service Broker statement is not the first statement in the batch."
this is from When should I use semicolons in SQL Server? which might be what you are looking for
which is in turn "straight a cut and paste"
I think this might be stack overflows version of a joke.
edit: ; are also after merge statements
Using the ; is the correct way to end one statement and start another. If you do not use the ; you may get unexpected results
It is to denote the end of an Sql statement.
Giving a list of situation that are 'required' is always prone to omission. There are other situations besides commont table expression and SSB which the parser cannot handle w/o semicolon terminating the previous statement, like THROW or WITH XMLNAMESPACES and the list is bound to grow.
The correct answer is: semicolon terminators are required and should always be put whenever writing new code. Period. Omitting them is tolerated, but is on the official deprecation list:
Deprecated feature: Transact-SQL Not ending Transact-SQL statements with a semicolon
Replacement: End Transact-SQL statements with a semicolon ( ; ).
Ignoring this deprecation warning is on your own peril.

Resources