使用多个线程的存储过程

时间:2016-10-27 14:39:42

标签: sql-server tsql

我的SQL存储过程遇到了一些性能问题。主要是由光标引起的。但我想不出在SQL存储过程中进行循环的任何不同方式。现在有没有办法使用服务器的多个线程来提高程序的性能?

这是我的存储过程的光标:

DECLARE FILE_CURSOR CURSOR FOR
  SELECT filenumber
  FROM   [fmsStage].[dbo].[file]
  WHERE  relationcode = @relationCode

OPEN FILE_CURSOR

FETCH NEXT FROM FILE_CURSOR INTO @fileID

WHILE @@FETCH_STATUS = 0
  BEGIN
      /**** Fetch data from outgoinginvoiceline for RelationCode and FileNumber ****/
      SET @amount = (SELECT DISTINCT Sum(cnt)
                     FROM   (SELECT Sum([fms].[dbo].[outgoinginvoiceline].[amount]) cnt
                             FROM   [fms].[dbo].[outgoinginvoiceline]
                             WHERE  [fms].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)
                                    AND [fms].[dbo].[outgoinginvoiceline].[relationcode] = @relationCode
                             UNION ALL
                             SELECT Sum([fmsAir].[dbo].[outgoinginvoiceline].[amount]) cnt
                             FROM   [fmsAir].[dbo].[outgoinginvoiceline]
                             WHERE  [fmsAir].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)
                                    AND [fmsAir].[dbo].[outgoinginvoiceline].[relationcode] = @relationCode
                             UNION ALL
                             SELECT Sum([fmsProjects].[dbo].[outgoinginvoiceline].[amount]) cnt
                             FROM   [fmsProjects].[dbo].[outgoinginvoiceline]
                             WHERE  [fmsProjects].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)
                                    AND [fmsProjects].[dbo].[outgoinginvoiceline].[relationcode] = @relationCode) AS counted)
      /**** Get the currency from the database ****/
      SET @currency = (SELECT TOP 1 [fms].[dbo].[outgoinginvoiceline].[currency]
                       FROM   [fms].[dbo].[outgoinginvoiceline]
                       WHERE  [fms].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)
                              AND [fms].[dbo].[outgoinginvoiceline].[relationcode] = @relationCode)

      /**** If the currency is not EURO then use the currencyrate on it, by default the currencyrate is 1.00 ****/
      IF @currency != 'EUR'
        BEGIN
            SET @currencyRate = (SELECT TOP 1 [fms].[dbo].[outgoinginvoiceline].[rate]
                                 FROM   [fms].[dbo].[outgoinginvoiceline]
                                 WHERE  [fms].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)
                                        AND [fms].[dbo].[outgoinginvoiceline].[relationcode] = @relationCode)
        END

      /**** If @amount is NULL (empty) then we set it to zero because adding NULL creates issues ****/
      IF @amount IS NULL
        BEGIN
            SET @amount = 0
        END

      /**** Do the amount times currencyRate ****/
      SET @amount = @amount * @currencyRate
      /**** Add the new amount to the total previous amount ****/
      SET @totalAmount = @totalAmount + @amount
      /**** Fetch data from outgoinginvoiceline for RelationCode and FileNumber ****/
      SET @amount2 = (SELECT DISTINCT Sum(cnt)
                      FROM   (SELECT Sum([fms].[dbo].[outgoinginvoiceline].[amount]) cnt
                              FROM   [fms].[dbo].[outgoinginvoiceline]
                              WHERE  [fms].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)
                              UNION ALL
                              SELECT Sum([fmsAir].[dbo].[outgoinginvoiceline].[amount]) cnt
                              FROM   [fmsAir].[dbo].[outgoinginvoiceline]
                              WHERE  [fmsAir].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)
                              UNION ALL
                              SELECT Sum([fmsProjects].[dbo].[outgoinginvoiceline].[amount]) cnt
                              FROM   [fmsProjects].[dbo].[outgoinginvoiceline]
                              WHERE  [fmsProjects].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID)) AS counted)
      /**** Get the currency from the database ****/
      SET @currency2 = (SELECT TOP 1 [fms].[dbo].[outgoinginvoiceline].[currency]
                        FROM   [fms].[dbo].[outgoinginvoiceline]
                        WHERE  [fms].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID))

      /**** If the currency is not EURO then use the currencyrate on it, by default the currencyrate is 1.00 ****/
      IF @currency2 != 'EUR'
        BEGIN
            SET @currencyRate2 = (SELECT TOP 1 [fms].[dbo].[outgoinginvoiceline].[rate]
                                  FROM   [fms].[dbo].[outgoinginvoiceline]
                                  WHERE  [fms].[dbo].[outgoinginvoiceline].[filenumber] = CONVERT(NVARCHAR, @fileID))
        END

      /**** If @amount is NULL (empty) then we set it to zero because adding NULL creates issues ****/
      IF @amount2 IS NULL
        BEGIN
            SET @amount2 = 0
        END

      /**** Do the amount times currencyRate ****/
      SET @amount2 = @amount2 * @currencyRate2
      /**** Add the new amount to the total previous amount ****/
      SET @totalAmount2 = @totalAmount2 + @amount2

      FETCH NEXT FROM FILE_CURSOR INTO @fileID
  END  

1 个答案:

答案 0 :(得分:3)

当您应该使用基于集合的解决方案时,您正在使用光标。基于集合的解决方案将类似于:

SELECT 
    fmsTotalAmount + fmsAirTotalAmount + fmsProjectsTotalAmount TotalAmount, 
    fmsRelationAmount + fmsAirRelationAmount + fmsProjectsRelationAmount TotalRelationAmount
FROM (

    SELECT 
        SUM(
            CASE WHEN fms1.currency != 'EUR' 
                THEN fms1.Amount * fms1.Rate
                ELSE ISNULL(fms1.Amount, 0) END) fmsTotalAmount,
        SUM(
            CASE WHEN fms1.relationcode = @relationCode 
                THEN 
                    CASE WHEN fms1.currency != 'EUR' 
                        THEN fms1.Amount * fms1.Rate
                        ELSE ISNULL(fms1.Amount, 0) END
                ELSE 0 END) fmsRelationAmount,
        SUM(
            CASE WHEN fmsAir1.currency != 'EUR' 
                THEN fmsAir1.Amount * fmsAir1.Rate
                ELSE ISNULL(fmsAir1.Amount, 0) END) fmsAirTotalAmount,
        SUM(
            CASE WHEN fmsProjects1.relationcode = @relationCode 
                THEN 
                    CASE WHEN fmsProjects1.currency != 'EUR' 
                        THEN fmsProjects1.Amount * fmsAir1.Rate
                        ELSE ISNULL(fmsProjects1.Amount, 0) END
                ELSE 0 END) fmsAirRelationAmount,
        SUM(
            CASE WHEN fmsProjects1.currency != 'EUR' 
                THEN fmsProjects1.Amount * fmsAir1.Rate
                ELSE ISNULL(fmsProjects1.Amount, 0) END) fmsProjectsTotalAmount,
        SUM(
            CASE WHEN fmsProjects1.relationcode = @relationCode 
                THEN 
                    CASE WHEN fmsProjects1.currency != 'EUR' 
                        THEN fmsProjects1.Amount * fmsProjects1.Rate
                        ELSE ISNULL(fmsProjects1.Amount, 0) END
                ELSE 0 END) fmsProjectsRelationAmount
    FROM   [fmsStage].[dbo].[file] f
    LEFT JOIN [fms].[dbo].[outgoinginvoiceline] fms1 ON 
        fms1.filenumber = CONVERT(NVARCHAR, f.filenumber)
    LEFT JOIN [fmsAir].[dbo].[outgoinginvoiceline] fmsAir1 ON 
        fmsAir1.filenumber = CONVERT(NVARCHAR, f.filenumber)
    LEFT JOIN [fmsProjects].[dbo].[outgoinginvoiceline] fmsProjects1 ON 
        fmsProjects1.filenumber = CONVERT(NVARCHAR, f.filenumber)
) a

虽然在没有看到您的架构/数据的情况下很难检查这是否正确。

要理解这一点,请从基表开始:

    SELECT *
    FROM   [fmsStage].[dbo].[file] f
    LEFT JOIN [fms].[dbo].[outgoinginvoiceline] fms1 ON 
        fms1.filenumber = CONVERT(NVARCHAR, f.filenumber)
    LEFT JOIN [fmsAir].[dbo].[outgoinginvoiceline] fmsAir1 ON 
        fmsAir1.filenumber = CONVERT(NVARCHAR, f.filenumber)
    LEFT JOIN [fmsProjects].[dbo].[outgoinginvoiceline] fmsProjects1 ON 
        fmsProjects1.filenumber = CONVERT(NVARCHAR, f.filenumber)

总和相当明显(这不能处理货币转换):

    SELECT SUM(ISNULL(fms1.Amount, 0)) fmsAmount, SUM(ISNULL(fmsAir1 .Amount, 0) fmsAirAmount --etc., etc.
    FROM   [fmsStage].[dbo].[file] f
    LEFT JOIN [fms].[dbo].[outgoinginvoiceline] fms1 ON 
        fms1.filenumber = CONVERT(NVARCHAR, f.filenumber)

然后了解case语句,它处理货币转换并按关系代码过滤:

    SELECT 
        fms1.Amount,
        CASE WHEN fms1.currency != 'EUR' 
            THEN fms1.Amount * fms1.Rate
            ELSE ISNULL(fms1.Amount, 0) END AmountValue,
        CASE WHEN fms1.relationcode = @relationCode 
            THEN 
               CASE WHEN fms1.currency != 'EUR' 
                   THEN fms1.Amount * fms1.Rate
                   ELSE ISNULL(fms1.Amount, 0) END
            ELSE 0 END AmountValueOnlyIfSameRelation
    FROM   [fmsStage].[dbo].[file] f
    LEFT JOIN [fms].[dbo].[outgoinginvoiceline] fms1 ON 
        fms1.filenumber = CONVERT(NVARCHAR, f.filenumber)

然后你将它们组合在一起。