• Oleg Netchaev (7/6/2010)


    Jeff Moden (7/2/2010)


    BWAA-HAA!!!! First, this is NOT a good example of Tally Table code. It doesn't use a Tally Table and it doesn't use anything that could be called efficient. Anyone who uses a recursive CTE to generate Tally numbers just doesn't know what hidden RBAR is. 😛

    I agree with what someone else has stated... this code should be used as an example of how NOT to write code and how NOT to accomplish a split. Same goes for any example in this thread that uses a recursive CTE to do the split. 😉

    I hope no one actually copies the code to use as a split function but I will say it was very interesting in how many "extra" things were added to the code in an attempt at obfuscation. Heh... maybe it should be titled "job security methods". :hehe:

    The urge to submit a post was burning me for a couple of days, but I decided to wait, because I was afraid that I will not be able to find the way to type what I think about the script in QotD without sounding rude, so I am glad that Jeff, Hugo and vk-kirov stood up to it. I agree that this is one of the finest examples how NOT TO DO something. There are some valid reasons to use recursive CTE, for example to write BOM queries in 2005 (HierarchyID is not yet available), but these reasons do not extend to creating a tally table, specifically with @text declared as varchar(500) but the recursion level restricted to 32767 (???).

    with tally (number) as

    (

    select

    top (len(@text))

    row_number() over (order by [object_id]) number

    from sys.objects

    )

    should do the trick right?

    Using xml to split delimited values is relatively cheap and not difficult to understand (Does not include the job security though :hehe:). Here is one example based on simply replacing the delimiter with closing and opening the node of arbitrary name and appending opening node to the head and closing to the tail:

    use AdventureWorks;

    go

    declare @delimiter char(1);

    declare @text nvarchar(500);

    declare @xml xml;

    -- set variable values

    select

    @delimiter = '.',

    @text = 'This t-sql will split these sentences into rows.' +

    'How many rows will be returned?.' + 'M.a.y.b.e..n.o.n.e.?',

    @xml = '<r>' + replace(@text, @delimiter, '</r><r>') + '</r>';

    -- here is the "split" in all its glory

    select

    item.value('text()[1]', 'varchar(100)') single_item

    from @xml.nodes('//r') R(item);

    The above happily returns desired output:

    single_item

    -----------------------------------------------

    This t-sql will split these sentences into rows

    How many rows will be returned?

    M

    a

    y

    b

    e

    NULL

    n

    o

    n

    e

    ?

    Oleg

    Hi Oleg,

    There are, indeed, a number of ways to create a Tally table including the one you used here and a similar one on another post of yours. But you do have to be careful. sys.Objects can contain very few objects. On a brand new database in 2005, it will only contain about 47 objects and even squaring that number will only return 2209 rows. Instead, my recommendation is to refer to Master.sys.All_Columns which will have at least 4000 rows in it on a full installation.

    So far as splits go, using one form of Tally Table or another to split smaller items (like INTs) is usually faster than trying to split such items with XML (I'm working on the tests to show you that on the other thread). Splitting larger items such as sentences is generally done faster (depending on the size) with the XML split. In either case, it's generally preferable to use an iTVF (inline Table Valued Function) over a stored procedure to add the utility of being able to pass a whole column to be split and not just a single variable.

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