The T-SQL Quiz

  • Jeff Moden (6/28/2008)


    dfalso (6/27/2008)


    In order to be truly safe, dynamic sql is needed, unless we could write a CTE with an input variable that governs the amount of recursion (again, on Monday maybe).

    Still, you haven't answered my question... How many times have you personally needed a Tally table larger than 11,000? And have you EVER heard of anyone needing over 121 million rows for a Tally table?

    Well, I don't think anyone needs over 121M, but I wouldn't presume to know what people do with it. Personally, I work with 1M because I have need to analyze dates and such. But that's just me. And putting a check in shouldn't hurt anything and is not hard to write.

  • Then, if you add a check, all this talk about dynamic SQL becomes a moot point. 😉

    --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)

  • Jeff Moden (6/27/2008)


    Matt Miller (6/27/2008)


    I personally roll with a tally table with a 1M limit, but that's a personal choice.

    Otherwise - it's very easy to come up with a numbers table of pretty much any size you need. CTE method coming up.

    ;with cte1 (N) as (select 1 union select 2), --2

    cte2 as (select c1.N from cte1 c1, cte1 c2, cte1 c3, cte1 c4), --16

    cte3 as (select c1.N from cte2 c1, cte2 c2, cte2 c3, cte2 c4), --65536

    cte4 as (select c1.N from cte3 c1, cte3 c2, cte3 c3, cte3 c4) --1.84467E+19

    select row_number() over (order by N) as Num

    from CTE4

    I like my method... it's about as fast and will easily make up to 121 million numbers... if you need more than that, just add 1 more cross-join... but, I submit... if you need more than a 121 million numbers, you're probably doing something wrong...

    DECLARE @Top INT

    SET @Top = 100000

    ;WITH cteTally

    AS (

    SELECT TOP(@Top) ROW_NUMBER() OVER(ORDER BY sc1.Type) AS N

    FROM Master.sys.SysColumns sc1,

    Master.sys.SysColumns sc2)

    SELECT *

    FROM cteTally

    Oh I know. It's just that I can more easily control the number I need to generate (when you get that big of a set you really don't want to generate an extra several BILLION rows if you don't need them). I have had somewhat regular need for tally up to about 1/2 million, which was why I kept it at 1M. After that though - I'd have to think something is severely wrong if you need a tally much bigger than that.

    Besides - I sometimes like to mix it up just a little so that I can remember why I prefer my tried and true methods....

    ----------------------------------------------------------------------------------
    Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?

  • Thanks Matt...

    Hey... like I said, take the exact code you posted and try to gen a 1000 rows... you'll see why...

    --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)

  • you mean the extra comma? ...hehe...:D

    Let me go correct that

    ----------------------------------------------------------------------------------
    Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?

  • That's the big thing... the other thing not so obvious is you don't show how to limit the output to any given number. 😉

    --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)

  • You're right of course - here's an actual useable version. This would create a temporary numbers table (1M rows), with the requisite index to make it all worthwhile...

    drop table #temp

    ;with cte1 (N) as (select 1 union select 2), --2

    cte2 as (select c1.N from cte1 c1, cte1 c2, cte1 c3, cte1 c4), --16

    cte3 as (select c1.N from cte2 c1, cte2 c2, cte2 c3, cte2 c4), --65536

    cte4(n) as (select null from cte3 c1,cte2 c2,cte2 c3)

    select top 1000000

    row_number() over (order by N) as Num

    into #temp

    from CTE4

    create unique clustered index pk_tmp on #temp(Num)

    ----------------------------------------------------------------------------------
    Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?

  • Matt Miller (6/28/2008)


    Oh I know. It's just that I can more easily control the number I need to generate (when you get that big of a set you really don't want to generate an extra several BILLION rows if you don't need them). I have had somewhat regular need for tally up to about 1/2 million, which was why I kept it at 1M.

    ...

    Besides - I sometimes like to mix it up just a little so that I can remember why I prefer my tried and true methods....

    Heh... ok... let's do like we always do... let's "mix it up just a little" for our mutual benefit and maybe for those that read our posts... 😀

    The following contains 3 "programmable" Tally tables... test harness (you'll recognize the method) is the same for all 3... Ask yourself some questions... Which ones run faster on the first run? Which ones run faster on subsequent runs? Which ones are easier to remember for coding purposes? Which ones are more readable?

    [font="Courier New"]DECLARE @Bitbucket INT

    DECLARE @TestSize INT

    SET @TestSize = 1000000

    SET STATISTICS TIME OFF

    SET STATISTICS IO OFF

    PRINT REPLICATE('=',100) ---------------------------------------------------------

    DBCC FREEPROCCACHE

    DBCC DROPCLEANBUFFERS

    PRINT '===== Matt Miller''s Method ====='

    SET STATISTICS IO ON

    SET STATISTICS TIME ON

    ;with cte1 (N) as (select 1 union select 2), --2

    cte2 as (select c1.N from cte1 c1, cte1 c2, cte1 c3, cte1 c4), --16

    cte3 as (select c1.N from cte2 c1, cte2 c2, cte2 c3, cte2 c4), --65536

    cte4(n) as (select null from cte3 c1,cte2 c2,cte2 c3)

    select top (@TestSize)

    @Bitbucket = row_number() over (order by N)

    from CTE4

    SET STATISTICS TIME OFF

    SET STATISTICS IO OFF

    PRINT REPLICATE('=',100) ---------------------------------------------------------

    DBCC FREEPROCCACHE

    DBCC DROPCLEANBUFFERS

    PRINT '===== Itzek''s Method ====='

    SET STATISTICS IO ON

    SET STATISTICS TIME ON

    ; WITH

    L0 AS (SELECT 1 AS C UNION ALL SELECT 1), --2 rows

    L1 AS (SELECT 1 AS C FROM L0 AS A, L0 AS B),--4 rows

    L2 AS (SELECT 1 AS C FROM L1 AS A, L1 AS B),--16 rows

    L3 AS (SELECT 1 AS C FROM L2 AS A, L2 AS B),--256 rows

    L4 AS (SELECT 1 AS C FROM L3 AS A, L3 AS B),--65536 rows

    L5 AS (SELECT 1 AS C FROM L4 AS A, L4 AS B),--4294967296 rows

    Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY C) AS n FROM L5)

    SELECT top (@TestSize) @Bitbucket = N FROM Nums

    SET STATISTICS TIME OFF

    SET STATISTICS IO OFF

    PRINT REPLICATE('=',100) ---------------------------------------------------------

    DBCC FREEPROCCACHE

    DBCC DROPCLEANBUFFERS

    PRINT '===== Jeff Moden''s Method'

    SET STATISTICS IO ON

    SET STATISTICS TIME ON

    ;WITH cTally AS

    (-----------------------------------------------------------------------------

    SELECT TOP (@TestSize)

    ROW_NUMBER() OVER (ORDER BY t1.ID) AS N

    FROM Master.sys.SysColumns t1

    CROSS JOIN Master.sys.SysColumns t2

    )-----------------------------------------------------------------------------

    SELECT @Bitbucket = N FROM cTally

    SET STATISTICS TIME OFF

    SET STATISTICS IO OFF

    [/font]

    --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)

  • Matt Miller (6/28/2008)


    It's just that I can more easily control the number I need to generate

    Just messing with you a bit now, ol' friend... It's pretty easy to control any of the methods, but consider the speed and the easy read (I'm a poet and don't know it 😛 )...

    [font="Courier New"]--===== Declare some local variables that could be parameters in a proc

    DECLARE @StartNumber INT,

            @EndNumber   INT,

            @Increment   INT

    --===== Set those "parameters" for demonstration purposes

         -- If you try to "trick" it, CTE will error as expected.

     SELECT @StartNumber = 1000, --Min if @Increment >1, Max if @Increment <1

            @EndNumber   = -100, --Max if @Increment >1, Min if @Increment <1

            @Increment   =   -3  --Must be (-) When @EndNumber < @StartNumber

    ;  WITH cteTally AS

    (-----------------------------------------------------------------------------

    --==== High performance CTE equivalent of a Tally or Numbers table with

         -- range and increment controls. --Jeff Moden

     SELECT TOP (((@EndNumber-@StartNumber)/@Increment)+1)

            ROW_NUMBER() OVER (ORDER BY t1.ID)

          * @Increment

          + (@StartNumber-@Increment) AS N

       FROM Master.sys.SysColumns t1

      CROSS JOIN Master.sys.SysColumns t2

    )-----------------------------------------------------------------------------

     SELECT N FROM cteTally [/font]

    --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)

  • All right - no problem. Under those circumstances - mine is actually the slowest of the three methods, then Brother Itzik's, with the fastest being the cross join.

    Why is mine the slowest (by far)? In short - one single word. By not using UNION ALL (even on such a trivial operation), mine tends to take twice as long as the other two. (I thought initially it was the multiple cross joins, but no. Add the work ALL to the initial UNION and mine ends back up in the same range as Itzik's).

    As to the other two - both are in fact very good, tend to vast amount of numbers rather quickly. The cross join on a system is a little faster; on the other hand, a lot of shops have prohibitions about using system objects in this fashion, so Itzik's method might be worth remembering.

    ----------------------------------------------------------------------------------
    Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?

  • Nah... After adding the "ALL", yours is just as fast, fewer lines to remember, and easier on the eyes.

    a lot of shops have prohibitions about using system objects in this fashion

    True enough... If they don't allow the exception, that's when I break out the porkchops 😛 The rest of the world is fairly oblivious... they don't do code reviews and the DBA (if there is one) either doesn't care (reason for no code reviews) or isn't responsible for the promotion of code.

    --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)

  • Any of this cow left for me? 🙂

    Here is my entry (really just a tweak of Jeff's):

    PRINT REPLICATE('=',100) ---------------------------------------------------------

    DBCC FREEPROCCACHE

    DBCC DROPCLEANBUFFERS

    declare @root int

    select @root = sqrt(@testsize)+1

    PRINT '===== RBarryYoung''s Method'

    SET STATISTICS IO ON

    SET STATISTICS TIME ON

    ;WITH cTally AS

    (-----------------------------------------------------------------------------

    SELECT TOP (@TestSize)

    ROW_NUMBER() OVER (ORDER BY t1.ID) AS N

    FROM (Select TOP (@root) ID from Master.sys.SysColumns) t1

    CROSS JOIN (Select TOP (@root) ID from Master.sys.SysColumns) t2

    )-----------------------------------------------------------------------------

    SELECT @Bitbucket = N FROM cTally

    SET STATISTICS TIME OFF

    SET STATISTICS IO OFF

    The strange thing is that I am getting much different numbers than you guys...

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • So now, what's the minimum number of rows guaranteed to be in master's syscolumns?

  • On 2005, it's always been 11,000+ wherever I've looked.

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • rbarryyoung (6/29/2008)


    Any of this cow left for me? 🙂

    Here is my entry (really just a tweak of Jeff's):

    Nicely done, Barry... but I get numbers just a bit slower (not much... about 20 ms on a million rows) because of the control you've put on the cross join... faster to let nature take its course.

    --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 - 271 through 285 (of 309 total)

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