While Loop for Inserts from Read-only Function

  • Hey guys,

    From a performance, I've read on here plenty of times to avoid Cursors and While Loops, but I was wondering if there is a better way in this case.

    Say I have a Function on a Production DB (where I can't view the code behind it). The only Input parameter for it is a date (last day of month).

    If I want to insert the values of that Function into a Tempdb table (I create a Dynamic Pivot off it hence avoiding table variables) for each Month End for, say, 6 months (it varies); is there a qucker way than creating the table, and then having a While Loop go through six iterations of inserting the Functions values into it (as well as changing the date each time)?

    Happy to provide the code on request but figured this was more a "Yes there is a better way, it's X" or "No", kind of question and that I'd write it from there.

    Thanks!

  • This will get rid of the function and the need to change the date each time, but it does use a WHILE loop, I have not attempted it to use a Tally table, just not enough time for me to do so

    DECLARE @m INT

    SET @m = 0

    WHILE @m < 13

    BEGIN

    select dateadd(dd,-1,dateadd(mm, datediff(mm, 0, GETDATE()) + @m, 0)) AS 'End of month'

    SET @m = @m + 1

    END

    For just about anything you want to do with dates visit:

    https://www.sqlservercentral.com/blogs/lynnpettis/archive/2009/03/25/some-common-date-routines.aspx[/ur]

    For using a Tally table see Jeff Moden's article at:

    http://www.sqlservercentral.com/articles/T-SQL/62867/

    If everything seems to be going well, you have obviously overlooked something.

    Ron

    Please help us, help you -before posting a question please read[/url]
    Before posting a performance problem please read[/url]

  • Sorry Bitbucket, I'm updating the Date as part of the While Loop (for 0 to @Months int) - e.g.

    set @StartDate = dateadd(d,-1,dateadd(m,1,(dateadd(d,1,@StartDate)))) It increases the Month that is fed into the Function by 1 and inserts the results into a table (which all works, if a little slowly).

    I'm just wondering if the While Loop's the fastest way for something like this. Thanks for flicking me the code though!

  • You don't mention if:

    1. What the function returns (datatype), and

    2. Whether this is a table-valued function, or a scalar-valued function.

    So, assuming that this is a table-valued function that returns a date, then this avoids cursors and while loops:

    DECLARE @Date datetime

    SET @Date = DateAdd(dd, DateDiff(dd, 0, GetDate()), 0)

    CREATE TABLE #temp (MyDate datetime)

    ;WITH TALLY (N) AS

    (

    SELECT TOP 366 ROW_NUMBER() OVER (ORDER BY object_id)

    FROM master.sys.columns

    )

    , DATES (N, MyDate) AS

    (

    SELECT N, DateAdd(d, -1*N, @Date)

    FROM TALLY

    UNION

    SELECT 0, @Date

    )

    INSERT INTO #temp (MyDate)

    SELECT f.ReturnField

    FROM dbo.MyFunction(d.MyDate) f

    CROSS JOIN DATES d

    If it's a scalar-valued function, change the insert to:

    INSERT INTO #temp (MyDate)

    SELECT dbo.MyFunction(d.MyDate)

    FROM DATES d

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • Sorry, the Function produces Fund Manager Information, e.g. Manager Name, Portfolios, Securities, Net Asset Values etc. It's table-based as you can therefore imagine. It's probably best if I include the main code for this section to show what i've done and give an idea of the columns:

    USE tempdb

    GO

    IF OBJECT_ID('dbo.MWH_Monthly') IS NOT NULL

    DROP TABLE dbo.MWH_Monthly

    declare @StartDate as datetime

    declare @MonthstoRun as int

    set @StartDate = '2009-01-31' -- ********* INPUTS

    set @MonthstoRun = 7 -- ********* INPUTS

    declare @Month as int

    set @Month = 0

    CREATE TABLE dbo.MWH_Monthly

    (

    MonthEnd datetime, [Portfolio Code] nvarchar(255), [Manager] nvarchar(255), [Asset Class] nvarchar(255),[Security Code] nvarchar(255), [Security Name] nvarchar(255),NAV decimal(18,4))

    while @Month < @MonthstoRun

    begin

    insert into dbo.MWH_Monthly

    select -- Column Names

    MonthEnd, [Portfolio Code], [Manager], [Asset Class],[Security Code], [Security Name],NAV

    FROM [db_name].dbo.[fn_BrowseManagerWarehouseDetails] (

    @StartDate

    ,''

    ,'')

    where PfolioOrder = 'AAAA'

    set @Month = @Month + 1

    set @StartDate = dateadd(d,-1,dateadd(m,1,(dateadd(d,1,@StartDate))))

    --break

    --else

    continue

    --end

    end

    The data from the Function would be pretty sensitive so I would have to dummy that data up, hence not providing it earlier (as I'm happy just to hear a method if there's one faster than requiring anyone to write me any code).

    To reiterate, can someone can confirm whether the While Loop would/would not perform the best performance for this kind of task. For 1402 rows (7 months worth of data), the query takes 13 seconds, and that's before I turn this whole thing into a Dynamic Pivot (have already done that, just mentioning it in terms of adding to the length of this process).

  • Nugby (2/22/2010)


    Sorry, the Function produces Fund Manager Information, e.g. Manager Name, Portfolios, Securities, Net Asset Values etc. It's table-based as you can therefore imagine.

    ...

    To reiterate, can someone can confirm whether the While Loop would/would not perform the best performance for this kind of task. For 1402 rows (7 months worth of data), the query takes 13 seconds, and that's before I turn this whole thing into a Dynamic Pivot (have already done that, just mentioning it in terms of adding to the length of this process).

    It increases the Month that is fed into the Function by 1 and inserts the results into a table (which all works, if a little slowly).

    A While Loop is going to do everything Row-By-Row. SQL works best as a set.

    In my earlier post, change the #Temp table to the layout of what the function returns, and then insert into that.

    DECLARE @Date datetime

    SET @Date = DateAdd(mm, DateDiff(mm, 0, GetDate()), 0) -- Beginning of this month

    CREATE TABLE #temp (Col1 datetime, Col2 float)

    ;WITH TALLY (N) AS

    (

    SELECT TOP 12 ROW_NUMBER() OVER (ORDER BY object_id)

    FROM master.sys.columns

    )

    , DATES (N, MyDate) AS

    (

    SELECT N, DateAdd(m, N, @Date)

    FROM TALLY

    UNION

    SELECT 0, @Date

    )

    INSERT INTO #temp (Col1, Col2)

    SELECT f.Col1, f.Col2

    FROM dbo.MyFunction(d.MyDate) f

    CROSS JOIN DATES d

    Why don't you test this out and see if it performs better?

    Edit: Changed to use next month versus previous month.

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • Thanks Wayne, will use that Tally Table for the Month Ends and see if it improves it tomorrow morning (just checking this before heading off to bed at the moment)!

  • Hi Wayne,

    I'm nearly there (for some reason, it took me ages to realise the correct way to get the correct month ends I needed in "dateadd(d,-1,dateadd(m,N,(dateadd(d,1,@StartDate))))". I just wasn't thinking to use "N" in there!).

    However much I play around with it though, I keep getting that simplest of all errors "The multi-part identifier "d.MyDate" could not be bound.". Have tried to use "Dates d", I created a temp table to pass it in, but I can't seem to make that part work and passing it in to the Function is the one parto f your code that I can't really test properly to deduce my error.

    declare @MonthsToRun int

    declare @StartDate datetime

    set @MonthstoRun = 7

    set @StartDate = '2009-01-31'

    CREATE TABLE dbo.MWH_Monthly

    (

    MonthEnd datetime, [Portfolio Code] nvarchar(255), [Manager] nvarchar(255), [Asset Class] nvarchar(255),[Security Code] nvarchar(255), [Security Name] nvarchar(255),NAV decimal(18,4))

    create table #temp (N int, MyDate datetime)

    ;WITH TALLY (N) AS

    (

    SELECT TOP(@MonthsToRun-1) ROW_NUMBER() OVER (ORDER BY object_id)

    FROM master.sys.columns

    )

    , DATES (N, MyDate) AS

    (

    SELECT N, dateadd(d,-1,dateadd(m,N,(dateadd(d,1,@StartDate))))

    FROM TALLY

    UNION

    SELECT 0, @StartDate

    )

    insert into #temp(N, MyDate)

    select N, MyDate from Dates

    --select * from dates

    INSERT INTO dbo.MWHMonthly (MonthEnd, [Portfolio Code], [Manager], [Asset Class],[Security Code], [Security Name],NAV)

    select

    --d.MyDate

    f.MonthEnd, -- Added in Edit

    f.[Portfolio Code], f.[Manager], f.[Asset Class],f.[Security Code], f.[Security Name],f.NAV

    FROM [dbo].[fn_BrowseManagerWarehouseDetails] (

    d.MyDate,'','') f -- forgot to mention, there's two other parameters that are always blank for my purposes

    CROSS JOIN #temp d -- Couldn't get "Dates d" working either

    select * from dbo.MWHMonthly

    DROP TABLE dbo.MWH_Monthly

    drop table #temp

    Can you see what I've done wrong? I suspect it's simpler than a lot of the stuff here, but it's not coming to me.

    ** Edit, realised I should insert MonthEnd from the Function into MWHMonthly table rather than d.MyDate, though it doesn't solve the problem

  • Nugby,

    I'm afraid that I gave you some bad advice.

    From BOL:

    TABLE

    Specifies that the return value of the table-valued function is a table. Only constants and @local_variables can be passed to table-valued functions.

    I'm very sorry about this.

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • That's alright. My original question had been about determining whether there was a better way to do this and it would appear that there isn't (barring converting the While Loop into some kind of "For each MyDate in Dates" to remove the @StartDate = dateadd etc" done in each iteration), so it looks as though my question was answered.

    I also got to extend my understand of Tally tables beyond making a 1 to 10 table. I may be able to use that Month End Dates for X months in the future, so definitely got some benefits out of the exercise.

    Thanks Wayne!

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

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