将基于游标的SQL转换为基于SET的操作

时间:2014-12-03 21:57:47

标签: sql sql-server

目前这个SQL需要大约1.5小时才能运行。我试图删除游标(使用CTE和递归),但我找不到合适的解决方案,因为它具有基于日期的运行总计。此外,还会在游标中删除数据

正如您在代码中看到的那样,我们在另一个中有两个游标。第一个光标中有40K记录。任何想法如何改进。

SET NOCOUNT ON
DECLARE @chg_type AS char(4);
DECLARE @chg_date AS datetime;
DECLARE @Salary AS char(9);
DECLARE @chg_factor AS numeric(13, 6);
DECLARE @to_Salary AS char(9);
DECLARE @Salary_permid AS char(9);
DECLARE @to_Salary_permid AS char(9);
DECLARE @income AS int;
DECLARE @cost AS numeric(13, 0);
DECLARE @ReportDate AS datetime;
DECLARE @lagDate AS datetime;
DECLARE @na AS numeric(18, 2);
DECLARE @system_date AS datetime;
DECLARE @count1 AS int;
DECLARE @counter AS int = 0;
SET @system_date = GETDATE();

DROP TABLE #Temptable_ReportDate
CREATE TABLE #Temptable_ReportDate (
     income int,
     Salary char(9),
     cost numeric(13, 0),
     ReportDate datetime
);
DROP TABLE #Temptable_LagDate
CREATE TABLE #Temptable_LagDate (
     income int,
     Salary char(9),
     cost numeric(13, 0),
     lagDate datetime
);
DROP TABLE #cc
CREATE TABLE #cc (
     chg_type char(4),
     chg_date datetime,
     Salary char(9),
     chg_factor numeric(13, 6),
     to_Salary char(9)
);
CREATE INDEX idx_c_ReportDate_income ON #Temptable_ReportDate (income)
CREATE INDEX idx_c_ReportDate_Salary ON #Temptable_ReportDate (Salary)
CREATE INDEX idx_c_lagDate_income ON #Temptable_LagDate (income)
CREATE INDEX idx_c_lagDate_Salary ON #Temptable_LagDate (Salary)
--Delete from #Temptable_ReportDate; 
--insert into #Temptable_ReportDate 
-- SELECT salaryHistory.income, salaryHistory.Salary, salaryHistory.cost, 
--   salaryHistory.ReportDate 
--  FROM 
--      dbo.salaryHistory salaryHistory 
--   inner join salaryMaster_full_view p on p.income = salaryHistory.income and p.ReportDate  = salaryHistory.ReportDate
--  ORDER BY salaryHistory.Salary
DECLARE salaryMaster_cursor CURSOR FOR
SELECT
     income,
     ReportDate,
     lagDate
FROM salaryMaster_full_view
WHERE ReportDate < @system_date
OR lagDate < @system_date
ORDER BY income
OPEN salaryMaster_cursor
BEGIN
     FETCH NEXT
     FROM salaryMaster_cursor
     INTO @income,
     @ReportDate,
     @lagDate
     WHILE @@Fetch_status = 0
     BEGIN
          SET @counter = @counter + 1;
          PRINT @counter;
          DELETE FROM #Temptable_ReportDate;
          DELETE FROM #Temptable_LagDate;
          INSERT INTO #Temptable_ReportDate
               SELECT
                    salaryHistory.income,
                    salaryHistory.Salary,
                    salaryHistory.cost,
                    salaryHistory.ReportDate
               FROM dbo.salaryHistory salaryHistory
               WHERE salaryHistory.income = @income
               AND salaryHistory.ReportDate = @ReportDate
               ORDER BY salaryHistory.Salary
          INSERT INTO #Temptable_LagDate
               SELECT
                    salaryHistory.income,
                    salaryHistory.Salary,
                    salaryHistory.cost,
                    salaryHistory.ReportDate
               FROM dbo.salaryHistory salaryHistory
               WHERE salaryHistory.income = @income
               AND salaryHistory.ReportDate = @lagDate
               ORDER BY salaryHistory.Salary
          --select * from #Temptable_ReportDate 
          IF @ReportDate < @system_date
          BEGIN
               DELETE FROM #cc;
               WITH hh_cte
               AS (SELECT
                    c.Salary,
                    c.chg_date,
                    c.to_Salary
               FROM SalaryChange c
               INNER JOIN #Temptable_ReportDate hh
                    ON hh.Salary = c.Salary
               --inner join salaryMaster p on hh.income = p.income and p.ReportDate = hh.ReportDate
               WHERE c.chg_type <> ('SUN')
               AND hh.income = @income
               -- and c.Salary = '204412100' 
               AND NOT (
               c.chg_type IN ('MON',
               'TUE',
               'WED')
               AND chg_factor = 0)
               AND chg_date > hh.ReportDate
               AND chg_date < @system_date),
               cap_change_cte
               AS (SELECT
                    c.*
               FROM hh_cte c
               UNION ALL
               SELECT
                    c.Salary,
                    c.chg_date,
                    c.to_Salary
               FROM SalaryChange c
               INNER JOIN cap_change_cte cc
                    ON cc.to_Salary = c.Salary
               --and c.to_Salary!= c.Salary 
               WHERE c.chg_type <> ('SUN')
               AND NOT (
               c.chg_type IN ('MON',
               'TUE',
               'WED')
               AND c.chg_factor = 0)
               -- AND c.chg_date > cc.ReportDate 
               AND c.chg_date < @system_date
               AND cc.chg_date < c.chg_date)
               INSERT INTO #cc
                    SELECT
                         chg_type,
                         chg_date,
                         Salary,
                         chg_factor,
                         to_Salary
                    FROM SalaryChange
                    WHERE Salary IN (SELECT DISTINCT
                         cc.Salary
                    FROM cap_change_cte cc)
               DECLARE salary_changes CURSOR FOR
               SELECT
                    cc.chg_type,
                    cc.chg_date,
                    cc.Salary,
                    cc.chg_factor,
                    cc.to_Salary
               FROM #cc cc
               ORDER BY cc.chg_date,
               cc.Salary;

               OPEN salary_changes
               FETCH NEXT
               FROM salary_changes
               INTO @chg_type,
               @chg_date,
               @Salary,
               @chg_factor,
               @to_Salary
               WHILE @@FETCH_STATUS = 0
               BEGIN
                    SET @count1 = (SELECT
                         COUNT(*)
                    FROM #Temptable_ReportDate
                    WHERE income = @income
                    AND Salary = @Salary);
                    IF (@count1 > 0)
                    BEGIN

                         DECLARE @newcost AS numeric(13, 0);
                         DECLARE @count AS int;
                         SET @newcost = ROUND(@cost * @chg_factor, 0);
                         IF @chg_type IN ('MON',
                              'TUE')
                         BEGIN
                              SET @count = (SELECT
                                   COUNT(*)
                              FROM #Temptable_ReportDate
                              WHERE income = @income
                              AND Salary = @to_Salary);
                              IF (@count > 0)
                                   UPDATE #Temptable_ReportDate
                                   SET cost = cost + @newcost,
                                       Salary = @to_Salary
                                   WHERE income = @income
                                   AND Salary = @Salary;

                              ELSE
                                   INSERT INTO #Temptable_ReportDate
                                        VALUES (@income, @to_Salary, @newcost, @ReportDate);

                         END
                         ELSE
                         BEGIN
                              IF @Salary = @to_Salary
                                   UPDATE #Temptable_ReportDate
                                   SET cost = @newcost
                                   WHERE income = @income
                                   AND Salary = @Salary
                              ELSE
                              BEGIN
                                   SET @count = (SELECT
                                        COUNT(*)
                                   FROM #Temptable_ReportDate
                                   WHERE income = @income
                                   AND Salary = @to_Salary);
                                   IF (@count > 0)
                                   BEGIN
                                        UPDATE #Temptable_ReportDate
                                        SET cost = cost + @newcost
                                        FROM #Temptable_ReportDate
                                        WHERE income = @income
                                        AND Salary = @to_Salary
                                        DELETE FROM #Temptable_ReportDate
                                        WHERE income = @income
                                             AND Salary = @Salary;

                                   END
                                   ELSE
                                        UPDATE #Temptable_ReportDate
                                        SET cost = @newcost,
                                            Salary = @to_Salary
                                        WHERE income = @income
                                        AND Salary = @Salary;

                              END
                         END
                    END
                    FETCH NEXT
                    FROM salary_changes
                    INTO @chg_type,
                    @chg_date,
                    @Salary,
                    @chg_factor,
                    @to_Salary
               END
               CLOSE salary_changes
               DEALLOCATE salary_changes
          END
          IF @lagDate < @system_date
          BEGIN
               DELETE FROM #cc;

               WITH hh_cte
               AS (SELECT
                    c.*
               FROM SalaryChange c
               INNER JOIN #Temptable_LagDate hh
                    ON hh.Salary = c.Salary
               --inner join salaryMaster p on hh.income = p.income and p.ReportDate = hh.ReportDate 
               WHERE c.chg_type <> ('SUN')
               AND hh.income = @income
               -- and c.Salary = '204412100' 
               AND NOT (
               c.chg_type IN ('MON',
               'TUE',
               'WED')
               AND chg_factor = 0)
               AND chg_date > hh.lagDate
               AND chg_date < @system_date),
               cap_change_cte
               AS (SELECT
                    c.*
               FROM hh_cte c
               UNION ALL
               SELECT
                    c.*
               FROM SalaryChange c
               INNER JOIN cap_change_cte cc
                    ON cc.to_Salary = c.Salary
               --and c.to_Salary!= c.Salary 
               WHERE c.chg_type <> ('SUN')
               AND NOT (
               c.chg_type IN ('MON',
               'TUE',
               'WED')
               AND c.chg_factor = 0)
               -- AND c.chg_date > cc.ReportDate 
               AND c.chg_date < @system_date
               AND cc.chg_date < c.chg_date)
               INSERT INTO #cc
                    SELECT
                         chg_type,
                         chg_date,
                         Salary,
                         chg_factor,
                         to_Salary
                    FROM SalaryChange
                    WHERE Salary IN (SELECT
                         cc.Salary
                    FROM cap_change_cte cc)
               DECLARE salary_changes CURSOR FOR
               SELECT
                    cc.chg_type,
                    cc.chg_date,
                    cc.Salary,
                    cc.chg_factor,
                    cc.to_Salary
               FROM #cc cc
               ORDER BY cc.chg_date,
               cc.Salary;

               OPEN salary_changes
               FETCH NEXT
               FROM salary_changes
               INTO @chg_type,
               @chg_date,
               @Salary,
               @chg_factor,
               @to_Salary
               WHILE @@FETCH_STATUS = 0
               BEGIN
                    SET @count1 = (SELECT
                         COUNT(*)
                    FROM #Temptable_LagDate
                    WHERE income = @income
                    AND Salary = @Salary);
                    IF (@count1 > 0)
                    BEGIN
                         SET @newcost = ROUND(@cost * @chg_factor, 0);
                         IF @chg_type IN ('MON',
                              'TUE')
                         BEGIN
                              SET @count = (SELECT
                                   COUNT(*)
                              FROM #Temptable_LagDate
                              WHERE income = @income
                              AND Salary = @to_Salary);
                              IF (@count > 0)
                                   UPDATE #Temptable_LagDate
                                   SET cost = cost + @newcost,
                                       Salary = @to_Salary
                                   WHERE income = @income
                                   AND Salary = @Salary;

                              ELSE
                                   INSERT INTO #Temptable_LagDate
                                        VALUES (@income, @to_Salary, @newcost, @lagDate);

                         END
                         ELSE
                         BEGIN
                              IF @Salary = @to_Salary
                                   UPDATE #Temptable_LagDate
                                   SET cost = @newcost
                                   WHERE income = @income
                                   AND Salary = @Salary
                              ELSE
                              BEGIN
                                   SET @count = (SELECT
                                        COUNT(*)
                                   FROM #Temptable_LagDate
                                   WHERE income = @income
                                   AND Salary = @to_Salary);
                                   IF (@count > 0)
                                   BEGIN
                                        UPDATE #Temptable_LagDate
                                        SET cost = cost + @newcost
                                        FROM #Temptable_LagDate
                                        WHERE income = @income
                                        AND Salary = @to_Salary
                                        DELETE FROM #Temptable_LagDate
                                        WHERE income = @income
                                             AND Salary = @Salary;

                                   END
                                   ELSE
                                        UPDATE #Temptable_LagDate
                                        SET cost = @newcost,
                                            Salary = @to_Salary
                                        WHERE income = @income
                                        AND Salary = @Salary;

                              END
                         END
                    END
                    FETCH NEXT
                    FROM salary_changes
                    INTO @chg_type,
                    @chg_date,
                    @Salary,
                    @chg_factor,
                    @to_Salary
               END
               CLOSE salary_changes
               DEALLOCATE salary_changes
          END;


          --select * from #Temptable_ReportDate;
          --select * from #Temptable_LagDate;
          INSERT INTO salary_current_test (income, Salary, cost, na, cost_change, change_na, ReportDate, lagDate, cost_date, hold, release, change)
               SELECT
                    ISNULL(#Temptable_ReportDate.income, #Temptable_LagDate.income) AS income,
                    ISNULL(#Temptable_ReportDate.Salary, #Temptable_LagDate.Salary) AS Salary,
                    SUM(ISNULL(#Temptable_ReportDate.cost, 0)) AS cost,
                    ISNULL(#Temptable_ReportDate.cost, 0) * 0 AS na,
                    SUM(ISNULL(#Temptable_ReportDate.cost, 0) - ISNULL(#Temptable_LagDate.cost, 0)) AS cost_change,
                    ISNULL(#Temptable_ReportDate.cost, 0) * 0 AS change_na,
                    ISNULL(#Temptable_ReportDate.ReportDate, @ReportDate) AS ReportDate,
                    ISNULL(#Temptable_LagDate.lagDate, @lagDate) AS lagDate,
                    GETDATE() AS cost_date,
                    0 AS hold,
                    0 AS release,
                    1 AS change
               FROM #Temptable_ReportDate
               FULL OUTER JOIN #Temptable_LagDate
                    ON #Temptable_ReportDate.Salary = #Temptable_LagDate.Salary
               GROUP BY ISNULL(#Temptable_ReportDate.income, #Temptable_LagDate.income),
                        ISNULL(#Temptable_ReportDate.Salary, #Temptable_LagDate.Salary),
                        ISNULL(#Temptable_ReportDate.cost, 0) * 0,
                        ISNULL(#Temptable_ReportDate.cost, 0) * 0,
                        ISNULL(#Temptable_ReportDate.ReportDate, @ReportDate),
                        ISNULL(#Temptable_LagDate.lagDate, @lagDate)
               ORDER BY 2

          FETCH NEXT
          FROM salaryMaster_cursor
          INTO @income,
          @ReportDate,
          @lagDate
     END
END
CLOSE salaryMaster_cursor
DEALLOCATE salaryMaster_cursor
SET NOCOUNT OFF
GO

0 个答案:

没有答案