6

This question isn't about order of executions. It's about just the ORDER BY.

In standard execution is:

  • FROM
  • WHERE
  • GROUP BY
  • HAVING
  • SELECT
  • ORDER BY
  • TOP

EDIT: This question has been more or less the issue of "Does SQL Server apply short circuit evaluation when executing ORDER BY expressions?" The answer is SOMETIMES! I just haven't found a reasonable reason as to why. See Edit #4.

Now suppose I have a statement like this:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  (
   SELECT
     MAX(PurchaseDateTime)
   FROM
     Purchases
   WHERE
     Purchases.CustomerID = Customers.CustomerID
  ) DESC --STATEMENT3

This isn't the real statement I'm trying to execute, but just an example. There are three ORDER BY statements. The third statement is only used for rare cases where the last name and first name match.

If there are no duplicate last names, does SQL Server not execute ORDER BY statements #2 and #3? And, logically, if there are no duplicate last name and first name, does SQL Server note execute statement #3.

This is really for optimization. Reading from the Purchases table should only be a last resort. In the case of my application, it wouldn't be efficient to read every single "PurchaseDateTime" from "Purchases" grouping by "CustomerID".

Please keep the answer related to my question and not a suggestion like building an index for CustomerID, PurchaseDateTime in Purchases. The real question is, does SQL Server skip unnecessary ORDER BY statements?

Edit: Apparently, SQL Server will always execute every statement as long as there is one row. Even with one row, this will give you a divide by zero error:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  1/(Contacts.ContactID - Contacts.ContactID) --STATEMENT3

Edit2: Apparently, this doesn't give divide by zero:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  CASE WHEN 1=0
    THEN Contacts.ContactID
    ELSE 1/(Contacts.ContactID - Contacts.ContactID)
  END --STATEMENT3

Well, the original answer to my question is YES, it does execute, but what's nice is that I can stop execute with a proper CASE WHEN

Edit 3: We can stop execution of an ORDER BY statement with a proper CASE WHEN. The trick, I guess, is to figure out how to use it properly. CASE WHEN will give me what I want, which a short circuit execution in an ORDER BY statement. I compared the Execution Plan in SSMS and depending on the CASE WHEN statement, the Purchases table isn't scanned at all EVEN THOUGH it's a clearly visible SELECT/FROM statement:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  CASE WHEN 1=0
    THEN
    (
     SELECT
       MAX(PurchaseDateTime)
     FROM
       Purchases
     WHERE
       Purchases.CustomerID = Customers.CustomerID
    )
    ELSE Customers.DateOfBirth
  END DESC

Edit 4: Now I'm completely confused. Here's an example by @Lieven

WITH Test (name, ID) AS
(SELECT 'Lieven1', 1 UNION ALL SELECT 'Lieven2', 2)

SELECT * FROM Test ORDER BY name, 1/ (ID - ID)

This yields no divide by zero, which means SQL Server does in fact, do short circuit evaluation on SOME tables, specifically those created with the WITH command.

Trying this with a TABLE variable:

DECLARE @Test TABLE
(
    NAME nvarchar(30),
    ID int
);
INSERT INTO @Test (Name,ID) VALUES('Lieven1',1);
INSERT INTO @Test (Name,ID) VALUES('Lieven2',2);
SELECT * FROM @Test ORDER BY name, 1/ (ID - ID)

will yield a divide by zero error.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ShortFuse
  • 5,970
  • 3
  • 36
  • 36
  • 2
    I sincerely doubt the database would be efficient in this case. The engine does not know what is unnecessary in the same way that you might, so it has no way to make that judgement. – Gordon Linoff Jul 13 '12 at 17:26
  • Just tried this you are right Gordon. – JonH Jul 13 '12 at 17:29
  • Does the query plan show the subquery being executed then passed into a Sort operation? I agree with @GordonLinoff - I doubt the DBMS would know to "skip" the correlated subquery operation. – N West Jul 13 '12 at 17:31
  • Apparently it does execute it. At first I thought it was because it was a subquery, but it executes whatever I put there. If I put (5/(Customers.CustomerID - CustomerID)) ASC I'll get a divide by zero error. Talk about inefficient! – ShortFuse Jul 13 '12 at 17:34
  • What I realize is there is some confusion. I think the SQL order of execution refers to the actual sorting. Prior to the sorting, it needs to resolve all the fields being sorted. I would expect SQL Server and all other databases to resolve these fields during the preceding steps, because the request is to order by three values. It is possible (though not at all guaranteed) that the actual standard is more detailed. By the way, I really like this example. – Gordon Linoff Jul 13 '12 at 17:38
  • @GordonLinoff I tried to stick it into an ORDER BY query to avoid having it being in the main FROM and having to make a JOIN. I know that would guarantee execution. But it seems like SQL Server (even 2012) will execute every order by statement even if not necessary (see my new edit). I can't figure out why you would want that. It even executes the ORDER BY statement when there is only ONE row! – ShortFuse Jul 13 '12 at 17:42
  • 1
    @ShortFuse - You might have duplicate Last/Firstnames. This statement works `;WITH Test (name, ID) AS (SELECT 'Lieven1', 1 UNION ALL SELECT 'Lieven2', 2) SELECT * FROM Test ORDER BY name, 1/ (ID - ID)` meaning the second order by clause **is not evaluated** but this statement bombs `;WITH Test (name, ID) AS (SELECT 'Lieven1', 1 UNION ALL SELECT 'Lieven1', 2) SELECT * FROM Test ORDER BY name, 1/ (ID - ID)` because of the duplicate name and sql-server **has** to evaluate the second order by clause. – Lieven Keersmaekers Jul 13 '12 at 17:45
  • 1
    Also note that the query optimizer **does not do** short circuit evaluation. The query optimizer is a **cost based optimizer**. This implies that depending on various circumstances *(statistics, indexes, join types, phase of the moon, ...)*, it can decide to follow a certain path or not. – Lieven Keersmaekers Jul 13 '12 at 17:52
  • @Lieven Very nice! and very fun. I tried it with a CASE WHEN (in the new edit2) and CASE WHEN does not allow execution (aka short circuit evaluation) – ShortFuse Jul 13 '12 at 17:52
  • 1
    @ShortFuse - that might not seem logical but is all related due to the fact that the optimizer is cost based. Moral of the story is that you should forget about short circuit evaluation, evaluate the performance characteristics of your statement with the expected data and possibly even re-evaluate after a month/year/... . – Lieven Keersmaekers Jul 13 '12 at 17:53
  • I think SQL will always fetch the values in the ORDER BY and sort them in the final step. In many scenario's 'post-fetching' those values would impose a serious performance penalty. – Paul Jul 13 '12 at 18:01
  • @ShortFuse - Regarding your latest edit: you *(should)* **must** forget about short circuit evaluation! You most likely **will** find a statement that works today, draw the **wrong** conclusion and I will guarantee it will not work anymore a year from now. – Lieven Keersmaekers Jul 13 '12 at 18:06
  • @Lieven Yeah, in the end, I'm going to have to just use in tables in memory. The ideal was try to get it all down in one statement, but it's not possible. The reality is I do want short-circuit evaluation, but it's not possible without building another table and doing it manually in another statement (IF THEN) – ShortFuse Jul 13 '12 at 18:13
  • @ShortFuse - Perhaps not what you had in mind but at least, it will keep you your sanity ;) – Lieven Keersmaekers Jul 13 '12 at 18:18
  • @Lieven You confused me so much now with the WITH Test line. Apparently, WITH has short circuited order by execution, but table variables don't. wat – ShortFuse Jul 13 '12 at 18:24
  • @ShortFuse: No you are not getting it. Neither of those things are true: the SQL optimizer can do either or neither of those things whenever it wants to. It makes that decision based largely on table statistics, so if your table has any changes at all, it could suddenly start to work the other way around. – RBarryYoung Jul 13 '12 at 18:57
  • @ShortFuse: You need to stop editing and changing this question over and over again. This is not a discussion site. If you have another question, then post it in another thread. If not, then I am going to vote to close this question (something I have never done before). – RBarryYoung Jul 13 '12 at 19:08
  • If you feel that you do need to discuss this, then I strongly suggest that you take it to http://www.SQLServerCentral.com, where you will get all the discussion on this topic that you can handle. – RBarryYoung Jul 13 '12 at 19:09
  • @RBarryYoung I'm not exactly changing the question. The original question became too broad. I thought your answer was correct originally, and then when I found the issue with the WITH table, I wasn't sure anymore. Edit your last post about statistics into you answer and I think this question can be closed. – ShortFuse Jul 13 '12 at 19:45
  • So what is the question here? Those edits make it sound like you're answering the question in the question (which, to say the least, is not how we do things 'round here). – George Stocker Jul 13 '12 at 19:58
  • @GeorgeStocker The question in the original text is: "The real question is, does SQL Server skip unnecessary ORDER BY statements?" I rephrased it as "Does SQL Server apply short circuit evaluation when executing ORDER BY expressions?" RBarryYoung give ma definitive NO, which I was ready to accept until I read Lieven's (non answer actually a comment) which leads to maybe. Lieven didn't really formulate it as an answer so I editted it back into the question and marked RBarryYoung's since he said he vote to close it. I'm done with this thread. – ShortFuse Jul 13 '12 at 21:48
  • 1
    @Lieven - Interesting example. The query optimiser can tell at compile time in this case though whether the internal table of constants contains any duplicates or not and thus whether the second order will affect the result. [I'd noticed before](http://stackoverflow.com/a/8640583/73226) that it seems to check for duplicates but hadn't seen this information used anywhere. – Martin Smith Jul 14 '12 at 08:34
  • @ShortFuse - In case you or anyone else missed it, SQL Server **does not do short circuit evaluation (sce)**. If my comments led you to believe it did then I have expressed myself poorly. The example was just to show that while it might look like it does sce with one set of values, the same statement will not sce with another set of values. Beware of drawing the wrong conclusions. – Lieven Keersmaekers Jul 14 '12 at 11:04
  • @MartinSmith - How I would love to say I knew all that... – Lieven Keersmaekers Jul 14 '12 at 11:11

3 Answers3

5

First of all what you are calling "Statements" are no such thing. They are sub-clauses of the ORDER BY (major) clause. The difference is important, because "Statement" implies something separable, ordered and procedural, and SQL sub-clauses are none of those things.

Specifically, SQL sub-clauses (that is, the individual items of a SQL major clause (SELECT, FROM, WHERE, ORDER BY, etc.)) have no implicit (nor explicit) execution order of their own. SQL will re-order them in anyway that it finds convenient and will almost always execute all of them if it execute any of them. In short, SQL Server does not do that kind of "short-circuit" optimizations because they are trivially effective and seriously get in the way of the very different kind of optimizations that it does do (i.e., Statistical Data Access/Operator Optimizations).

So the correct answer to your original question (which you should not have changed) is NO, not reliably. You cannot rely on SQL Server to not use some sub-clause of the ORDER BY, simply because it looks like it does not need to.

The only common exception to this is that the CASE function can (in most circumstances) be used to short-circuit execution paths (within the CASE function though, not outside of it), but only because it is specifically designed for this. I cannot think of anything else in SQL that you can rely on to act like this.

RBarryYoung
  • 55,398
  • 14
  • 96
  • 137
0
DECLARE @MyTable TABLE
(
  Data varchar(30)
)

INSERT INTO @MyTable (Data) SELECT 'One'
INSERT INTO @MyTable (Data) SELECT 'Two'
INSERT INTO @MyTable (Data) SELECT 'Three'

--SELECT *
--FROM @MyTable
--ORDER BY LEN(Data), LEN(Data)/0
  -- Divide by zero error encountered.

SELECT *
FROM @MyTable
ORDER BY LEN(Data), CASE WHEN Data is null THEN LEN(Data)/0 ELSE 1 END
  -- no problem

Also with SET STATISTICS IO ON I saw these results:

SELECT *
FROM @MyTable
ORDER BY LEN(Data)
--(3 row(s) affected)
--Table '#4F2895A9'. Scan count 1, logical reads 1    

SELECT *
FROM @MyTable
ORDER BY LEN(Data), CASE WHEN Data = 'One' THEN (SELECT MAX(t2.Data) FROM @MyTable t2) ELSE Data END
--(3 row(s) affected)
--Table '#4F2895A9'. Scan count 2, logical reads 2

SELECT *
FROM @MyTable
ORDER BY LEN(Data), CASE WHEN Data = 'Zero' THEN (SELECT MAX(t2.Data) FROM @MyTable t2) ELSE Data END
--(3 row(s) affected)
--Table 'Worktable'. Scan count 0, logical reads 0
--Table '#4F2895A9'. Scan count 1, logical reads 1
Amy B
  • 108,202
  • 21
  • 135
  • 185
0

I guess you have answered your question. However, why you are sorting the data on just firstname, lastname and if these two are same then purchase order otherwise you will do on DOB?

Logically, it should be firstname, lastname, DOB. If these three are the same, only then should you evaluate the purchaseorderdate. There are many people who have the same names, but very few have the same names and DOBs. This will reduce the time you will be querying the purchase table.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gulli Meel
  • 891
  • 4
  • 6