Replace a Cursor

  • Hi All

    Could anyone help me on getting the below Cursor query convert to a Recursive CTE or with a while loop as I do not want to use a cursor.

    Declare @Companyname Nvarchar (400)

    declare @STR nvarchar(MAX)

    TRUNCATE TABLE STAGING.dbo.[IT_G_L Entry]

    DECLARE GLEntry_cursor CURSOR FOR

    SELECT REPLACE Name FROM Company where Name <> 'AAAAA'

    OPEN GlEntry_cursor

    FETCH NEXT FROM GLEntry_cursor INTO @Companyname

    WHILE @@FETCH_STATUS = 0

    BEGIN

    SET @STR = ('SELECT '''+@CompanyName+''' as Company,[Entry No_] ,[G_L Account No_] ,[Posting Date] ,[Document Type] FROM dbo.['+@CompanyName+'$G_L Entry]')

    --PRINT (@STR)

    Insert into STAGING.dbo.[IT_G_L Entry]

    EXEC (@STR)

    FETCH NEXT FROM GLEntry_cursor INTO @Companyname

    END

    CLOSE GLEntry_cursor

    DEALLOCATE GLEntry_cursor

    Regards

    SR

  • ksrikanth77 (7/2/2014)


    Hi All

    Could anyone help me on getting the below Cursor query convert to a Recursive CTE or with a while loop as I do not want to use a cursor.

    Declare @Companyname Nvarchar (400)

    declare @STR nvarchar(MAX)

    TRUNCATE TABLE STAGING.dbo.[IT_G_L Entry]

    DECLARE GLEntry_cursor CURSOR FOR

    SELECT REPLACE Name FROM Company where Name <> 'AAAAA'

    OPEN GlEntry_cursor

    FETCH NEXT FROM GLEntry_cursor INTO @Companyname

    WHILE @@FETCH_STATUS = 0

    BEGIN

    SET @STR = ('SELECT '''+@CompanyName+''' as Company,[Entry No_] ,[G_L Account No_] ,[Posting Date] ,[Document Type] FROM dbo.['+@CompanyName+'$G_L Entry]')

    --PRINT (@STR)

    Insert into STAGING.dbo.[IT_G_L Entry]

    EXEC (@STR)

    FETCH NEXT FROM GLEntry_cursor INTO @Companyname

    END

    CLOSE GLEntry_cursor

    DEALLOCATE GLEntry_cursor

    Regards

    SR

    Why do you want to replace one type of loop with another? Honestly, a well written cursor is likely to perform better than a recursive cte or a while loop.

    _______________________________________________________________

    Need help? Help us help you.

    Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

    Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.

    Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
    Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
    Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
    Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/

  • First, you might ask if you really need to replace the cursor. Are there so many company records that it makes a significant difference in comparison to the time it takes to insert the records? Is it worth compromising working code?

    But if you must... 🙂

    I have two methods for your consideration. Both concatenate all the insert statements into a single string using a different string concatenation method.

    1. Uses the FOR XML PATH('') trick. Downside is that you should be sure that the Company.Name doe not have any escaped XML characters. (&,",', etc.) If these are strictly alphanumeric fields, you should be good.

    DECLARE @STR nvarchar(MAX);

    TRUNCATE TABLE STAGING.dbo.[IT_G_L Entry];

    SET @STR =

    (SELECT 'INSERT INTO STAGING.dbo.[IT_G_L Entry] SELECT ''' + CompanyName

    + ''', [Entry No_], [G_L Account No_], [Posting Date], [Document Type] FROM dbo.[' + CompanyName + '$G_L Entry];'

    FROM Company

    WHERE CompanyName <> 'AAAAA'

    FOR XML PATH(''));

    EXEC @STR;

    Or...

    2. Uses the string variable as an accumulation point.

    DECLARE @STR nvarchar(MAX);

    TRUNCATE TABLE STAGING.dbo.[IT_G_L Entry];

    SELECT @STR = COALESCE(@str,'') + 'INSERT INTO STAGING.dbo.[IT_G_L Entry] SELECT ''' + Name

    + ''', [Entry No_], [G_L Account No_], [Posting Date], [Document Type] FROM dbo.[' + Name + '$G_L Entry];'

    FROM Company

    WHERE Name <> 'AAAAA';

    EXEC @STR;

    The only other issue that I'm aware of is if there is any possibility of SQL injection via the Company.Name field. I'm not sure how to secure these against it. Perhaps the wiser denizens of the forum might be able to help.

    I am only an egg.

  • Hi James

    I am not getting any result or any error when I execute the second query

  • James Hammond (7/2/2014)


    The only other issue that I'm aware of is if there is any possibility of SQL injection via the Company.Name field. I'm not sure how to secure these against it. Perhaps the wiser denizens of the forum might be able to help.

    Since @CompanyName is also part of the table name it would be easy to leverage sys.objects to see if that table exists.

    if EXISTS(

    select *

    from sys.objects

    where name = '[' + @CompanyName + '$G_L Entry]'

    )

    --Put your dynamic code here

    You could even add an else to raise an exception if the value passed in doesn't exist (that is probably something I would do).

    _______________________________________________________________

    Need help? Help us help you.

    Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

    Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.

    Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
    Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
    Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
    Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/

  • Since @CompanyName is also part of the table name it would be easy to leverage sys.objects to see if that table exists.

    ...

    or use OBJECTPROPERTY function.

    No cursor or loop requried to build SQL, however the performance of this query will be defined not by loop slowness, but the fact that it still needs to be a seria of separate insert queries as you need to insert from different tables:

    DECLARE @sql NVARCHAR(MAX) = ''

    TRUNCATE TABLE STAGING.dbo.[IT_G_L Entry]

    SELECT @sql = @sql + 'Insert into STAGING.dbo.[IT_G_L Entry] SELECT '''+C.Name+''',[Entry No_] ,[G_L Account No_] ,[Posting Date] ,[Document Type] FROM dbo.['+@CompanyName+'$G_L Entry];

    '

    FROM Company C

    WHERE C.Name != 'AAAAA'

    AND OBJECTPROPERTY(OBJECT_ID( 'dbo.['+C.Name+'$G_L Entry]'),'IsUserTable') = 1

    EXEC (@sql)

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • ksrikanth77 (7/2/2014)


    Hi James

    I am not getting any result or any error when I execute the second query

    It produces what I expect on my database. Try replacing the EXEC with PRINT and see what string is being created. Post that result here, if you can't find anything wrong with it.

    I agree with everyone else posting here that you won't see any performance improvement by making this change. The only benefit I can see is if you have a company mandate against cursors.

  • Eugene Elutin (7/3/2014)


    Since @CompanyName is also part of the table name it would be easy to leverage sys.objects to see if that table exists.

    ...

    or use OBJECTPROPERTY function.

    ...

    Sean and Eugene - Good idea. I like that!

  • Looking at the code you provided I am wondering why you want to eliminate the cursor. You are looping through a set of tables to import the data from each source table in a set based manner.

    What problem are you experiencing.

    Yes, you could write the code so that it creates a single dynamic sql based code set that would import the data from the tables all at once but if you aren't having any serious issues I'm not sure if I would take the time as much as I don't really like cursors.

    They do have a use, just make sure you are using them correctly.

  • Yes, you could write the code so that it creates a single dynamic sql based code set that would import the data from the tables all at once...

    Yep, building @sql with UNION ALL bit between SELECT, instead of INSERT.

    Would it make any faster? I doubt it...

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • Eugene Elutin (7/3/2014)


    Yes, you could write the code so that it creates a single dynamic sql based code set that would import the data from the tables all at once...

    Yep, building @sql with UNION ALL bit between SELECT, instead of INSERT.

    Would it make any faster? I doubt it...

    Plus, the way you have it right now each table is imported in its own transaction not as one massive transaction which could actually be slower and would be an all or nothing import if the insert failed for any reason.

  • Thanks all you for your responses, however When I print or Exec the result is Command Completed Successfully.

    My request is

    I have a company table with the Name Column having Company Names

    I want to loop through all the GLtables which are separated by companyname prefix and to insert data into a staging table getting data from all the GLtables from different companies.

    The solution provided do not have a @CompanyName varaible Declared and gives me an error for creating the variable.And If I declare and set the company name it displays me the insert into ... select * from! query prints correctly.

    I am looking for a solution which exactly replace the cursors which I posted earlier.

    Thanks Again

    SR

  • ksrikanth77 (7/3/2014)


    Thanks all you for your responses, however When I print or Exec the result is Command Completed Successfully.

    My request is

    I have a company table with the Name Column having Company Names

    I want to loop through all the GLtables which are separated by companyname prefix and to insert data into a staging table getting data from all the GLtables from different companies.

    The solution provided do not have a @CompanyName varaible Declared and gives me an error for creating the variable.And If I declare and set the company name it displays me the insert into ... select * from! query prints correctly.

    I am looking for a solution which exactly replace the cursors which I posted earlier.

    Thanks Again

    SR

    Why are you wanting to replace the cursor? You still haven't answered that question. Is there a specific problem or issue you are trying to solve?

  • Lynn Pettis (7/3/2014)


    Eugene Elutin (7/3/2014)


    Yes, you could write the code so that it creates a single dynamic sql based code set that would import the data from the tables all at once...

    Yep, building @sql with UNION ALL bit between SELECT, instead of INSERT.

    Would it make any faster? I doubt it...

    Plus, the way you have it right now each table is imported in its own transaction not as one massive transaction which could actually be slower and would be an all or nothing import if the insert failed for any reason.

    Again, why replace the cursor? Is it not functioning correctly? In your original post you said you wanted to use a recursive cte or a while loop. Both of which are very likely to be slower to produce the same results. A cursor is really just a while loop.

    --edit--

    got bitten by the quote bug. :hehe:

    _______________________________________________________________

    Need help? Help us help you.

    Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

    Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.

    Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
    Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
    Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
    Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/

  • And if you are so serious about eliminating the cursor, here is some code. Try it by itself in an empty test database. I have the insert part commented so it won't run.

    SET NOCOUNT ON;

    create table dbo.Company(Name varchar(10));

    insert into dbo.Company values ('ABC'),('BCD');

    declare @SQLCmd nvarchar(max);

    select @SQLCmd = stuff((select N'union all' + nchar(13) + nchar(10) + N'select ''' + c.Name + N''' as Company, [Entry No_], [G_L Account No_], [Posting Date], [Document Type] FROM dbo.[' +

    c.Name + N'$G_L Entry]' + nchar(13) + nchar(10)

    from dbo.Company c

    where c.Name <> 'AAAAA'

    order by c.Name

    for xml path(''),TYPE).value('.','nvarchar(max)'),1,11,'');

    print @SQLCmd;

    --truncate table STAGING.dbo.[IT_G_L Entry];

    --insert into STAGING.dbo.[IT_G_L Entry]

    --exec (@SQLCmd);

    drop table dbo.Company;

    SET NOCOUNT OFF:

    EDIT: Added the truncate table to the code, also as a comment.

Viewing 15 posts - 1 through 15 (of 20 total)

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