This is my first post, so please bear with me. I'm trying to tackle a problem regarding invoicing in an application I'm developing. Big picture view: an administrator can create custom rules that will trigger certain fees on a set of users that will be invoiced. The rules will be stored in the database as T-SQL fragments that will then be applied to a select statement using dynamic SQL. When the admin generates the invoice it can contain multiple fees and therefore multiple rules. The end result is user information and the final bill amount which is the sum of all the fees for the user (depending on if they meet rule criteria). There is also a pro-rated calculation that comes into play.Problem: I'm using nested cursors to build a dynamic sql statement that is executed, and the results are put into a temp table. I know cursors in general can hamper performance, and nested cursors hurt even more. In my tests with real production data of 70 users and 2 rule combinations, the query can take up to 40 seconds and that is after using the tuning wizard (for what it's worth). I'd like to see if there is a more efficient way to do this, possibly without cursors.I've attached the schema creation scripts and the stored procedure, as well as the way to execute. I've simplified everything down to a few fields, but in the production version I have more data that is included in the final output as well as different billing calculations.Thanks!Rick-- CREATE THE TABLES-- stores the report nameCREATE TABLE [dbo].[ReportHeader]( [ReportID] [int] NOT NULL, [ReportName] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL) ON [PRIMARY]GO-- stores the fee nameCREATE TABLE [dbo].[FeeHeader]( [FeeID] [int] NOT NULL, [FeeName] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL) ON [PRIMARY]GO-- links the reports to the fees. a report can have multiple feesCREATE TABLE [dbo].[FeeLink]( [ReportID] [int] NULL, [FeeID] [int] NULL) ON [PRIMARY]GO-- the fee details including the ruleCREATE TABLE [dbo].[FeeDetails]( [FeeID] [int] NULL, [FeeDollar] [money] NULL CONSTRAINT [DF_a-Billing_GlobalFee_BG_FeeDollar] DEFAULT ((0)), [FeeRule] [nvarchar](max) COLLATE SQL_Latin1_General_CP1_CI_AS NULL) ON [PRIMARY]GO-- user informationCREATE TABLE [dbo].[UserTable]( [UserID] [int] NULL, [Handed] [varchar](5), [ProRate] [decimal](5,2)) ON [PRIMARY]GO-- LOAD THE TABLESINSERT INTO [dbo].[ReportHeader] (ReportID, ReportName)VALUES (1, 'Management Fee Report')INSERT INTO [dbo].[ReportHeader] (ReportID, ReportName)VALUES (2, 'Misc Fee Report')INSERT INTO [dbo].[FeeHeader] (FeeID, FeeName)VALUES (1, 'Right-Handed Fee')INSERT INTO [dbo].[FeeHeader] (FeeID, FeeName)VALUES (2, 'Left-Handed Fee')INSERT INTO [dbo].[FeeHeader] (FeeID, FeeName)VALUES (3, 'Misc Fee')INSERT INTO [dbo].[FeeLink] (ReportID, FeeID)VALUES (1,1)INSERT INTO [dbo].[FeeLink] (ReportID, FeeID)VALUES (1,2)INSERT INTO [dbo].[FeeLink] (ReportID, FeeID)VALUES (2,3)-- this fee is only applied to right-handed usersINSERT INTO [dbo].[FeeDetails] (FeeID, FeeDollar, FeeRule)VALUES (1, 20.00, '(UserTable.Handed = ''LEFT'')')-- this fee is only applied to left-handed usersINSERT INTO [dbo].[FeeDetails] (FeeID, FeeDollar, FeeRule)VALUES (2, 10.00, '(UserTable.Handed = ''RIGHT'')')-- this fee is applied to everyoneINSERT INTO [dbo].[FeeDetails] (FeeID, FeeDollar, FeeRule)VALUES (3, 5.00, null)INSERT INTO [dbo].[UserTable] (UserID, Handed, ProRate)VALUES (1, 'LEFT', 1.00)INSERT INTO [dbo].[UserTable] (UserID, Handed, ProRate)VALUES (2, 'RIGHT', 1.50)INSERT INTO [dbo].[UserTable] (UserID, Handed, ProRate)VALUES (3, 'LEFT', 0.50)INSERT INTO [dbo].[UserTable] (UserID, Handed, ProRate)VALUES (4, 'RIGHT', 1.00)INSERT INTO [dbo].[UserTable] (UserID, Handed, ProRate)VALUES (5, 'LEFT', 1.00)-- STORED PROCEDURECREATE PROCEDURE [dbo].[BillGenerate] -- Add the parameters for the stored procedure here @ReportId intASBEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; declare @userid int declare @prorate decimal(5,2) declare @feedollar decimal(16,2) declare @feerule varchar(max) declare @sql varchar(max) -- temp table to hold the results CREATE TABLE #FeeReport ( UserID int, BillAmount money ) -- select all the users to bill DECLARE c CURSOR FOR select UserID, ProRate from UserTable OPEN c FETCH NEXT FROM c INTO @userid, @prorate WHILE @@FETCH_STATUS = 0 BEGIN -- select all the fees for the report we are generating DECLARE d CURSOR FOR SELECT FD.FeeDollar, FD.FeeRule FROM FeeDetails FD inner join FeeLink FL on FD.FeeID = FL.FeeID inner join ReportHeader RH on FL.ReportID = RH.ReportID WHERE RH.ReportID = @ReportId open d FETCH NEXT FROM d INTO @feedollar, @feerule WHILE @@FETCH_STATUS = 0 BEGIN -- build the dynamic sql. if the rule is null (meaning it applies to everyone) -- I use '1=1' to always make it a true condition set @sql = 'INSERT INTO #FeeReport(UserID, BillAmount) Select UserId, ' set @sql = @sql + '(ProRate * ' + convert(varchar, @feedollar)+ ') as ToBill ' set @sql = @sql + ' from UserTable' set @sql = @sql + ' where UserID = ' + convert(varchar,@userid) +' AND (' set @sql = @sql + isnull(@feerule, '1=1') set @sql = @sql + ')' -- print for testing print (@sql) exec (@sql) FETCH NEXT FROM d INTO @feedollar, @feerule END CLOSE d DEALLOCATE d FETCH NEXT FROM c INTO @userid, @prorate END CLOSE c DEALLOCATE c-- create the output. I need to sum the results and only have one record per userSELECT UserId, Sum(BillAmount) as BillAmountFROM (SELECT DISTINCT * from #FeeReport) As FeeTableGROUP BY UserIDENDGO-- TEST the process by executing these:exec BillGenerate 1exec BillGenerate 2