Are the posted questions getting worse?

  • Jeff Moden (6/16/2009)


    Now, if we could just get them to say that UPDATEs always work in clustered index order... 😛

    They won't. The unofficial word from one of the guys who works on the optimiser is that there is nothing that enforces update order. If that's true then the fact that it currently does always1 update in clustered index order is just a side-effect of other behaviour.

    (1): In all cases that have been observed.

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • GilaMonster (6/17/2009)


    In all cases that have been observed.

    Maybe not 😉

    USE tempdb;

    GO

    CREATE TABLE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    (

    row_idINTIDENTITY(1,1)PRIMARY KEY CLUSTERED,

    costINTNOT NULL,

    running_totalINTNOT NULL DEFAULT 0

    );

    GO

    CREATE TRIGGER [trg_976231EF-DA13-4A8A-91DC-7D8931A2C14B_IOI]

    ON dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    AFTER INSERT

    AS

    BEGIN

    IF@@ROWCOUNT = 0 RETURN;

    SETROWCOUNT 0;

    SETNOCOUNT ON;

    DECLARE@total INT;

    SET@total = 0;

    UPDATETest

    SET@total = Test.running_total = Test.cost + @total

    FROMinserted I

    JOINdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS Test

    ON (Test.row_id = I.row_id);

    END;

    GO

    INSERTdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] (cost)

    SELECTTOP (10)

    FLOOR(RAND(CHECKSUM(NEWID())) * 1000)

    FROMmaster.sys.all_columns C1

    CROSS

    JOINmaster.sys.all_columns C2

    GO

    SELECTrow_id, cost, running_total

    FROMdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    ORDERBY

    row_id;

    GO

    DROP TABLE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B];

    Results:

    [font="Courier New"]row_idcostrunning_total

    14595254

    27904795

    32024005

    48053803

    52882998

    64372710

    77772273

    89501496

    9273546

    10273273[/font]

    Actual execution plan attached. :w00t:

    Paul

  • GilaMonster (6/16/2009)


    I just got word from the documentation people. The lack of the UPDATE <alias> was simply an oversight and it will be corrected in a future version of BoL.

    While I'm on the subject...

    I understand it is just a documentation oversight - but if we commit ourselves to never using 'undocumented' syntax (Barry!), how should we write an UPDATE from a self-join without an alias?

    Re-using the example I just posted:

    UPDATEdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    SETcost = 0

    FROMdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS A

    JOINdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS B ON B.row_id = A.row_id

    [font="Courier New"]Msg 8154, Level 16, State 1, Line 1

    The table 'dbo.976231EF-DA13-4A8A-91DC-7D8931A2C14B' is ambiguous.

    [/font]

    UPDATEA

    SETcost = 0

    FROMdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS A

    JOINdbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS B ON B.row_id = A.row_id

    [font="Courier New"](10 row(s) affected)[/font]

    🙂

    edit: reworded to hopefully emphasize the humour.

  • Paul,

    Check out the difference between the two pieces of code below and their behaviour:

    CREATE TABLE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    (

    row_id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,

    cost INT NOT NULL,

    running_total INT NOT NULL DEFAULT 0

    );

    GO

    CREATE TRIGGER [trg_976231EF-DA13-4A8A-91DC-7D8931A2C14B_IOI]

    ON dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    AFTER INSERT

    AS

    BEGIN

    IF @@ROWCOUNT = 0 RETURN;

    SET ROWCOUNT 0;

    SET NOCOUNT ON;

    DECLARE @total INT;

    SET @total = 0;

    UPDATE Test SET

    @total = Test.running_total = Test.cost + @total

    FROM

    inserted I

    INNER JOIN dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS Test

    ON (Test.row_id = I.row_id);

    END;

    GO

    INSERT dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] (cost)

    SELECT TOP (10)

    FLOOR(RAND(CHECKSUM(NEWID())) * 1000)

    FROM master.sys.all_columns C1

    CROSS

    JOIN master.sys.all_columns C2

    GO

    SELECT row_id, cost, running_total

    FROM dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    ORDER BY

    row_id;

    GO

    DROP TABLE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B];

    GO

    CREATE TABLE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    (

    row_id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,

    cost INT NOT NULL,

    running_total INT NOT NULL DEFAULT 0

    );

    GO

    CREATE TRIGGER [trg_976231EF-DA13-4A8A-91DC-7D8931A2C14B_IOI]

    ON dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    AFTER INSERT

    AS

    BEGIN

    IF @@ROWCOUNT = 0 RETURN;

    SET ROWCOUNT 0;

    SET NOCOUNT ON;

    DECLARE @total INT;

    SET @total = 0;

    UPDATE Test SET

    @total = Test.running_total = Test.cost + @total

    FROM

    inserted I

    INNER JOIN dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS Test WITH (INDEX(0),TABLOCKX)

    ON (Test.row_id = I.row_id);

    END;

    GO

    INSERT dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] (cost)

    SELECT TOP (10)

    FLOOR(RAND(CHECKSUM(NEWID())) * 1000)

    FROM master.sys.all_columns C1

    CROSS

    JOIN master.sys.all_columns C2

    GO

    SELECT row_id, cost, running_total

    FROM dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    ORDER BY

    row_id;

    GO

    DROP TABLE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B];

    GO

  • Lynn,

    Well yes, quite. The previous discussion on this emphasized that INDEX(0) was not required.

    Indeed, it is still not sufficient, try:

    INNER LOOP JOIN dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS Test WITH (INDEX(0),TABLOCKX)

    The rows seem to 'naturally' come out of the inserted pseudo-table in reverse clustered index order - this was the behaviour I had not seen before.

    Of course one could also drive the query from Test by reversing the join and so on, but...

    Paul

  • Ooo! Just found a better way to break INDEX(0):

    SET FORCEPLAN ON

    INSERT dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] (cost)

    SELECT TOP (10)

    FLOOR(RAND(CHECKSUM(NEWID())) * 1000)

    FROM master.sys.all_columns C1

    CROSS

    JOIN master.sys.all_columns C2

    GO

    That produces the loop join and reverse-ordering, without changing the trigger. Ouch!

    Paul

  • However, you are forcing the order of the update by INSERTED, not the table you are updating. So, are you really breaking the quirky update that allows for a one million row update to process in about ten seconds?

    I'd really like to hear what Jeff has to say on this aspect.

  • Flip the order of the tables in the trigger and the the update occurs properly based on the clustered index on the table being updated even with the force plan. So, it also comes down to the position of the tables in the FROM clause.

  • Hehe, some are really mind-boggling

  • Lynn Pettis (6/17/2009)


    Flip the order of the tables in the trigger and the the update occurs properly based on the clustered index on the table being updated even with the force plan. So, it also comes down to the position of the tables in the FROM clause.

    Yep. I have been quite a proponent of the UPDATE method in the past, but I must admit I am now concerned. The list of requirements (the ones I can remember immediately) is now:

      No parallelism

      Correct clustered index

      INDEX(0)

      TABLOCKX - maybe

      No loop join unless the join order is forced to drive from the table with the clustered index (triggers only?)

    I wish Jeff were online!

    edit:

    Reversing the written order of the tables is not sufficient - one must also either use INNER LOOP JOIN or OPTION (FORCE ORDER, LOOP JOIN). The former produces a warning unless FORCE ORDER is also specified, and they both force the join order of all tables that might be specified - that might be awkward in some circumstances, for example with a sub-select in the SELECT list, or with an EXISTS caluse.

  • Paul White (6/17/2009)


    GilaMonster (6/17/2009)


    In all cases that have been observed.

    Maybe not 😉

    Weren't you the one who said earlier in this thread that you'd never seen an update that didn't work in clustered index order?

    I understand it is just a documentation oversight - but if we commit ourselves to never using 'undocumented' syntax (Barry!), how should we write an UPDATE from a self-join without an alias?

    Re-using the example I just posted:

    UPDATE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    SET cost = 0

    FROM dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS A

    JOIN dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS B ON B.row_id = A.row_id

    Msg 8154, Level 16, State 1, Line 1

    The table 'dbo.976231EF-DA13-4A8A-91DC-7D8931A2C14B' is ambiguous.

    As per books online:

    If the object being updated appears more than one time in the FROM clause, one, and only one, reference to the object must not specify a table alias. All other references to the object in the FROM clause must include an object alias.

    This works just fine

    UPDATE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    SET cost = 0

    FROM dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    JOIN dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS B ON B.row_id = dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B].row_id

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • Paul White (6/17/2009)


    Yep. I have been quite a proponent of the UPDATE method in the past, but I must admit I am now concerned. The list of requirements (the ones I can remember immediately) is now:

      No parallelism

      Correct clustered index

      INDEX(0)

      TABLOCKX - maybe

      No loop join unless the join order is forced to drive from the table with the clustered index (triggers only?)

    And even then there's still no guarantee that it will work in the next hotfix, CU, SP or version.

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • GilaMonster (6/17/2009)


    Weren't you the one who said earlier in this thread that you'd never seen an update that didn't work in clustered index order?

    Yes indeed. And that was rather my point. I have now! Posted it as soon as I came across it. Quite shocked I was!

    I understand it is just a documentation oversight - but if we commit ourselves to never using 'undocumented' syntax (Barry!), how should we write an UPDATE from a self-join without an alias?

    GilaMonster (6/17/2009)


    This works just fine

    UPDATE dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    SET cost = 0

    FROM dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B]

    JOIN dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B] AS B ON B.row_id = dbo.[976231EF-DA13-4A8A-91DC-7D8931A2C14B].row_id

    Oh yes of course. Not as pretty maybe, but it works, so... 😎

    Apologies to Barry :blush:

    edit: several, for wording, to fix an unclosed quote, and well stuff really

  • GilaMonster (6/17/2009)


    And even then there's still no guarantee that it will work in the next hotfix, CU, SP or version.

    Oh I wouldn't worry about that.

    The good news is that we can all now write code that depends on rows coming out of the inserted and deleted 'tables' in reverse-clustered index order! 😀 😀 😀

  • GilaMonster (6/17/2009)


    Jeff Moden (6/16/2009)


    Now, if we could just get them to say that UPDATEs always work in clustered index order... 😛

    They won't. The unofficial word from one of the guys who works on the optimiser is that there is nothing that enforces update order. If that's true then the fact that it currently does always1 update in clustered index order is just a side-effect of other behaviour.

    (1): In all cases that have been observed.

    Yeppp. Agreed. Just wishful thinking on my part. 🙂

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

Viewing 15 posts - 5,791 through 5,805 (of 66,712 total)

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