Code within separate IF/ELSE IF statements executed/parsed?

  • I am trying to populate a temp table from the sysjobhistory of various servers based on a parameter in a procedure. I thought it would be fairly straightforward:

    DECLARE @server sysname

    SET @server = 'bovril'

    IF (@server = 'bovril') BEGIN

    SELECT *

    INTO #hist

    FROM msdb.dbo.sysjobhistory

    END

    ELSE IF (@server = 'lowenbrau') BEGIN

    SELECT *

    INTO #hist

    FROM lowenbrau.msdb.dbo.sysjobhistory

    END

    ELSE IF (@server = 'sfqcnt2') BEGIN

    SELECT *

    INTO #hist

    FROM sfqcnt2.msdb.dbo.sysjobhistory

    END

    Unfortunately I get:

    Msg 2714, Level 16, State 1, Line 12

    There is already an object named '#hist' in the database.

    Msg 2714, Level 16, State 1, Line 17

    There is already an object named '#hist' in the database.

    It seems to be trying to run, the code even when the condition is not met.

    I have an easy enough solution:

    SELECT *

    INTO #hist

    FROM msdb.dbo.sysjobhistory

    WHERE 1=2

    SET INDENTITY_INSERT #hist ON

    IF (@server = 'bovril') BEGIN

    INSERT INTO #hist(.....)

    SELECT *

    FROM msdb.dbo.sysjobhistory

    END

    ELSE IF.....

    I remain exceedingly puzzled as to the behaviour however, and was hoping someone could explain to further my understanding.

    [font="Arial"]_______________________________________________________
    Change is inevitable... Except from a vending machine.[/font]

  • Hi Rob

    SQL Server analyzes your statement before it becomes executed. Your first IF-block uses INSERT INTO ... Both other blocks use SELECT INTO. Since your temp table was already created above the IF part this cannot be executed.

    Use INSERT INTO within your other IF blocks and everything should work find.

    Greets

    Flo

  • Hey Rob,

    Check this out 🙂

    --

    -- This doesn't work

    --

    DECLARE @Choice INTEGER;

    SET @Choice = 3;

    IF @Choice = 1

    BEGIN

    SELECT TOP (1) a = 1 INTO #A FROM sys.databases

    END

    ELSE IF @Choice = 2

    BEGIN

    SELECT TOP (1) a = 2 INTO #A FROM sys.databases

    END

    ELSE IF @Choice = 3

    BEGIN

    SELECT TOP (1) a = 3 INTO #A FROM sys.databases

    END;

    GO

    --

    -- This does

    --

    DECLARE @Choice INTEGER;

    SET @Choice = 3;

    SELECT TOP (1)

    a = 1

    INTO #A

    FROM sys.databases

    WHERE @Choice = 1

    UNION ALL

    SELECT TOP (1)

    a = 2

    FROM sys.databases

    WHERE @Choice = 2

    UNION ALL

    SELECT TOP (1)

    a = 3

    FROM sys.databases

    WHERE @Choice = 3

    GO

    SELECT * FROM #a;

    GO

    DROP TABLE #a;

  • ...compare the plan for the example that does work (with the UNIONs) against the plan for this:

    --

    -- This also works

    --

    DECLARE @Choice INTEGER;

    SET @Choice = 3;

    IF @Choice = 1

    BEGIN

    SELECT a = 1 INTO #A FROM sys.databases

    END

    ELSE IF @Choice = 2

    BEGIN

    SELECT a = 2 INTO #B FROM sys.databases

    END

    ELSE IF @Choice = 3

    BEGIN

    SELECT a = 3 INTO #C FROM sys.databases

    END;

    You should see that SQL Server compiles the whole batch (for re-use) so it can't produce a single plan in the case where all the tables are named #A, even though logically only one path can be taken. In the case with the UNIONs, the various inputs are concatenated before creating table #A once from the result, so it is easy for the server to generate a nice re-usable plan.

    Let me know if that doesn't make sense to you 😉

  • Yeah, makes sense.

    I have the union one in there anyway because I need to reference by same table name later on.

    It's basically for a wee html dashboard of jobs on the various servers so it's not going to get hit that hard anyway but it's always handy to look at performance issues on these things which I shall do in more detail when I get the chance.

    Cheers.

    [font="Arial"]_______________________________________________________
    Change is inevitable... Except from a vending machine.[/font]

  • Cool. The other option would be dynamic SQL, but personally I like the UNION.

  • Yes. Though I did not specifically mention so I was aiming to intimate that I was avoiding the dynamic where possible.

    Now for the fun stuff, the DHTML tooltip of the actual error on hover.

    [font="Arial"]_______________________________________________________
    Change is inevitable... Except from a vending machine.[/font]

  • Rob Fisk (11/6/2009)


    Yes. Though I did not specifically mention so I was aiming to intimate that I was avoiding the dynamic where possible.

    Yeah - I'm naturally anti-red-text too.

    Rob Fisk (11/6/2009)


    Now for the fun stuff, the DHTML tooltip of the actual error on hover.

    If you think DHTML is fun, you need to get out more :laugh:

  • Oh, I do the strangest things for fun. The DHTML stuff was mainly code theft so the fun bit was actually seeing the end result in action and now we have a nice jobs dashboard that emails us of any failures, even those on the old 2k boxes we don't have an old office license to make email work properly with.

    One of my "for fun" projects involved some hideous red text, a procedure to generate insert statements for any table. As you can probably imagine, the nested escaping of single quotes got somewhat crazy.

    [font="Arial"]_______________________________________________________
    Change is inevitable... Except from a vending machine.[/font]

Viewing 9 posts - 1 through 8 (of 8 total)

You must be logged in to reply to this topic. Login to reply