Question

Since I could not find it out here, do set statements happen in order scripted? Below is query I found. Would I get the same result every time it ran?

DECLARE @Num VARCHAR(10) = '10';
DECLARE @Cleaned VARCHAR(10);
SET @Cleaned = SUBSTRING(@Num, PATINDEX('%[^0]%', @Num+'.'), LEN(@Num));
SET @Cleaned = CASE
                   WHEN LEN(@Cleaned) > 3
                   THEN @Cleaned
                   ELSE RIGHT('00000'+@Cleaned, 3)
               END;
-- to be set up
SELECT @Num AS [before]
     , @Cleaned AS [After]
Was it helpful?

Solution

Yes, you will get the same results in every case. There are a few exceptions where SQL Server doesn't seem to process the code in the order the code is written, and a few cases where there are no guarantees at all. Some are intentional and some are unintuitive.

GOTO

GOTO b2;

b1: 
PRINT 'first';
GOTO b3;

b2: 
PRINT 'second';
GOTO b1;

b3:
PRINT 'done';

Results:

second
first
done

This might not be expected simply because these labels can get lost in more complex code, can be hit only in certain conditions, etc.

Deferred Name Resolution

CREATE PROCEDURE dbo.floob AS
  SELECT * FROM dbo.ThisTableDoesNotExist;

This is kind of related in that it allows you to create an object at compile time that references an object that will cause the procedure to fail when you call it. I mention it only because it allows you to create this procedure before you create the table that the procedure will ultimately depend on.

SQLCMD mode

There is a mode in SSMS that allows you to intersperse SQLCMD and T-SQL. SSMS will make a first pass at the code and process all the SQLCMD bits, and then make a second pass where it will process the T-SQL. Let's say you have a table with an identity column, and a file that includes an insert into this table. With the following script run in SQLCMD mode, the filename.sql query will get executed on the first pass, and the insert of Bob will happen on the second pass. So the IDENTITY values will be generated in the opposite order:

:CONNECT instance
INSERT dbo.table(name) VALUES('Bob');
:r filename.sql

Compile Time Name Checking

IF 1=1
  CREATE TABLE #foo1(x int);
ELSE
  CREATE TABLE #foo1(y int);

Result:

Msg 2714, Level 16, State 1, Line 4
There is already an object named '#foo1' in the database.

The parser has gone ahead to line 4 and prevented you from creating two objects of the same name, even though that would never have been possible at runtime due to the IF conditional never being able to evaluate as true (line 2 was never executed).

Order of execution of a single statement

Depending on the version and compatibility level of SQL Server, you may see various cases where clauses are not evaluated in the order you expect. E.g.:

CREATE TABLE #x(y varchar(32));

INSERT #x(y) VALUES('foo'),(4);

SELECT y FROM #x
  WHERE ISNUMERIC(y) = 1
    AND y < 5;

Result:

Msg 245, Level 16, State 1, Line 5
Conversion failed when converting the varchar value 'foo' to data type int.

SQL Server tried to evaluate the y < 5 predicate before the ISNUMERIC() result could filter out ineligible rows. People often first try to use a CTE or correlated subquery, but that often fails because you still can't control the order in which these things are evaluated. Typical workarounds include using TRY_CONVERT() or a CASE expression (example here, and more on CASE in a minute), while more elaborate and less desirable approaches exist (such as dumping the filtered rows to a #temp table first).

CASE + AGG

While in most cases a CASE expression is the only surefire way to guarantee order of evaluation (misconceptions abound, though), even that can fail in some scenarios, one of which was discussed here at great length:

Example that fails, even though the ELSE should never be reached:

DECLARE @i INT = 1;
SELECT CASE WHEN @i = 1 THEN 1 ELSE MIN(1/0) END;

Result:

Msg 8134, Level 16, State 1
Divide by zero error encountered.

JOIN order

For completeness I want to make sure it is clear that if your query says...

SELECT ... FROM t1
  INNER JOIN t2
  INNER JOIN t3

...SQL Server is free to perform those joins in whatever order is most efficient, unless you use OPTION (FORCE ORDER) or any other hint that implicitly forces order. So you may see join strategies that optimize a join between t1 and t3, and then join those results to t2.

Surely, there are others...

...those are just a few off the cuff.

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top