SQLCLR运行太慢

时间:2014-08-12 10:50:59

标签: sql-server performance sqlclr

我在表中有160万行。有100,000行,缺少一些信息。找不到重复引用的信息。我编写了一个SQL CLR程序。 它以非常慢的速度执行。在30分钟内只有100,000行处理100,000个行。

以下代码可以替换为Inline SQL。

  var paymentSql =String.Format("select PaymodeId,StdLedgerId,BaseAmount,RegNo/*,REfInstno,RefStdLedgerId,RefPaymodeId*/ from vw_Payment_Ledger_Matching_Other {0} {1}" ,(condition.Equals("") ? "" : " where " + condition) ," order by CenterId,Ledgerdate,RecptKey ");

                var payment = new SqlCommand(paymentSql, conn1) { CommandTimeout = 600 };

                using (SqlDataReader payments = payment.ExecuteReader())
                {
                    while (payments.Read())
                    {
                        var paymentPaymodeId = payments["PaymodeId"];
                        var paymentStdLedgerId = payments["StdLedgerId"];
                        var paymentAmount = payments["BaseAmount"];
                        var paymentRegNo = payments["RegNo"];
                        //var paymentRefInstNo = payments["RefInstNo"];
                        //var paymentRefStdLedgerId = payments["RefStdLedgerId"];
                        //var paymentRefPayModeId = payments["RefPayModeId"];

                        //if (Convert.ToInt32(paymentRefInstNo) == 0 && Convert.ToInt32(paymentRefStdLedgerId) == 0 && paymentRefPayModeId.Equals("0"))
                        {
                            var ledgerSql = String.Format("select paymodeId,StdLedgerId,Instno,Concession,LumpSump,ConcessionDtl,LumpSumpDtl from vw_Payment_Ledger_Matching_inst a where a.regno='{0}' and abs(a.BaseAmount) between  abs({1})-5 and abs({1})+5 and Isnull(a.refInstno,0)=0 and a.insttype<>'O'" +
                                                   "and (cast(a.StdLedgerID as varchar(10))+cast(InstNo as varchar(1))) not in ( select cast(b.refStdLedgerID as varchar(10))+cast(b.refInstNo as varchar(1)) from vw_Payment_Ledger_Matching_inst b"
                                                   +" where b.regno='{0}'  and (b.BaseAmount) between  ({1})-5 and ({1})+5 and b.Insttype='O' )"
                                                   +" order by a.CenterId,a.RecptKey,a.LedgerDate ",paymentRegNo,paymentAmount );
                            var Ledger = new SqlCommand(ledgerSql, conn2) { CommandTimeout = 600 };
                            SqlDataReader ledger = Ledger.ExecuteReader();
                            if (ledger.Read())
                            {
                                var ledgerPayModeId = ledger["PayModeID"];                                    
                                var ledgerStdLedgerId = ledger["StdLedgerId"];
                                var ledgerInstNo = ledger["InstNo"];
                                var ledgerConcession = ledger["Concession"];
                                var ledgerLumpsump = ledger["Lumpsump"];
                                var ledgerConcessionDtl = ledger["ConcessionDtl"];
                                var ledgerLumpsumpDtl = ledger["LumpsumpDtl"];

                                var updatesql = "update " + updateTable + " set RefInstno=" + ledgerInstNo
                                                + ", RefStdLedgerId=" + ledgerStdLedgerId + ""
                                                + ", RefPayModeId='" + ledgerPayModeId + "'"
                                                + ", RefConcession=" + ledgerConcession
                                                + ", RefLumpsump=" + ledgerLumpsump
                                                + ", RefConcessionDtl=" + ledgerConcessionDtl
                                                + ", RefLumpsumpDtl=" + ledgerLumpsumpDtl
                                                + " where stdLedgerId=" + paymentStdLedgerId
                                                + " and PayModeId='" + paymentPaymodeId + "'";
                                var ledgerUpdate = new SqlCommand(updatesql, conn3);
                                ledgerUpdate.ExecuteNonQuery();                                    
                            }
                        }
                    }
                }

1 个答案:

答案 0 :(得分:1)

这里的问题不是SQLCLR。问题是当绝对没有理由这样做时,正在使用SQLCLR。除非我遗漏了某些内容,否则此操作只是一个简单的游标,对SELECT FROM vw_Payment_Ledger_Matching_inst查询返回的每一行执行UPDATE {updateTable}SELECT FROM vw_Payment_Ledger_Matching_Other。即使3 SqlConnection正在使用Context Connect = true;,它仍在执行非参数化查询(正如@usr在对问题的评论中指出的那样)创建一个新的SqlDataReader(即ledger 为这100,000个行中的每一行关闭。但同样,没有理由在这里使用SQLCLR。

让我们来看看这个操作正在尝试做什么。它说:

For each record in Query A
{
  Get a row from Query B
  Update a table via Query C, using the row from Query B
}

C#中的内容在语义/操作上等同于以下T-SQL:

CREATE PROCEDURE DoStuffBetter
(
   @Condition NVARCHAR(500),
   @UpdateTable NVARCHAR(500)
)
AS
SET NOCOUNT ON;

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'

DECLARE @PaymentPayModeId NVARCHAR(50),
        @PaymentStdLedgerId INT,
        @PaymentAmount MONEY,
        @PaymentRegNo NVARCHAR(50);


DECLARE payment CURSOR FOR
     SELECT PayModeId, StdLedgerId, BaseAmount, RegNo
                              /*, RefInstNo, RefStdLedgerId, RefPayModeId*/
     FROM   vw_Payment_Ledger_Matching_Other
' + CASE WHEN @Condition <> '' THEN N'     WHERE ' + @Condition ELSE '' END + N'
     ORDER BY CenterId, LedgerDate, RecptKey;

OPEN payment;

FETCH NEXT
FROM  payment
INTO  @PaymentPayModeId, @PaymentStdLedgerId, @PaymentAmount, @PaymentRegNo;

WHILE (@@FETCH_STATUS = 0)
BEGIN
     DECLARE @LedgerPayModeId NVARCHAR(50),
             @LedgerStdLedgerId INT,
             @LedgerInstNo INT,
             @LedgerConcession MONEY,
             @LedgerLumpSump MONEY,
             @LedgerConcessionDtl MONEY,
             @LedgerLumpsumpDtl MONEY;

     SELECT TOP 1
            @LedgerPayModeId = PayModeId,
            @LedgerStdLedgerId = StdLedgerId,
            @LedgerInstNo = InstNo,
            @LedgerConcession = Concession,
            @LedgerLumpSump = LumpSump,
            @LedgerConcessionDtl = ConcessionDtl,
            @LedgerLumpsumpDtl = LumpSumpDtl
     FROM   vw_Payment_Ledger_Matching_inst a
     WHERE  a.RegNo = @PaymentRegNo
     AND    ABS(a.BaseAmount) BETWEEN ABS(@PaymentAmount) - 5
                                  AND ABS(@PaymentAmount) + 5
     AND    ISNULL(a.RefInstNo, 0) = 0
     AND    a.InstType <> ''O''
     AND    (CAST(a.StdLedgerID AS VARCHAR(10)) + CAST(a.InstNo AS VARCHAR(1)))
            NOT IN (
                    SELECT CAST(b.RefStdLedgerID AS VARCHAR(10)) +
                           CAST(b.RefInstNo AS VARCHAR(1))
                    FROM   vw_Payment_Ledger_Matching_inst b
                    WHERE  b.RegNo = @PaymentRegNo
                    AND    (b.BaseAmount) BETWEEN (@PaymentAmount) - 5
                                              AND (@PaymentAmount) + 5
                    AND    b.InstType = ''O''
                   )
     ORDER BY a.CenterId, a.RecptKey, a.LedgerDate;

     IF (@@ROWCOUNT > 0)
     BEGIN
          UPDATE ' + @UpdateTable + N'
          SET    RefInstNo = @LedgerInstNo,
                 RefStdLedgerId = @LedgerStdLedgerId,
                 RefPayModeId = @LedgerPayModeId,
                 RefConcession = @LedgerConcession,
                 RefLumpsump = @LedgerLumpSump,
                 RefConcessionDtl = @LedgerConcessionDtl,
                 RefLumpsumpDtl = @LedgerLumpsumpDtl
          WHERE  StdLedgerId = @PaymentStdLedgerId
          AND    PayModeId = @PaymentPayModeId;
     END;

     FETCH NEXT
     FROM  payment
     INTO  @PaymentPayModeId, @PaymentStdLedgerId, @PaymentAmount, @PaymentRegNo;
END;

CLOSE payment;
DEALLOCATE payment;
';

EXEC (@SQL);

上面应该比C#版本更有效,但是仍然可以改进以删除CURSOR。以下基于集合的方法应该在逻辑上等效,但所有方法都在一个查询中完成:

CREATE PROCEDURE DoStuffBest
(
   @Condition NVARCHAR(500),
   @UpdateTable NVARCHAR(500)
)
AS
SET NOCOUNT ON;

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'

;WITH Payment AS
(
     SELECT PayModeId, StdLedgerId, BaseAmount, RegNo
                      /*, RefInstNo, RefStdLedgerId, RefPayModeId*/
     FROM   vw_Payment_Ledger_Matching_Other
' + CASE WHEN @Condition <> '' THEN N'     WHERE ' + @Condition ELSE '' END + N'
     ORDER BY CenterId, LedgerDate, RecptKey
), Ledger AS
(
     SELECT 
            a.PayModeId,
            a.StdLedgerId,
            a.InstNo,
            a.Concession,
            a.LumpSump,
            a.ConcessionDtl,
            a.LumpSumpDtl,
            Payment.PayModeId AS [PaymentPayModeId], -- passthrough for UPDATE
            Payment.StdLedgerId AS [PaymentStdLedgerId], -- passthrough for UPDATE
            ROW_NUMBER() OVER (PARTITION BY a.RegNo
                 ORDER BY a.CenterId, a.RecptKey, a.LedgerDate) AS [RowNumInGroup]
     FROM   vw_Payment_Ledger_Matching_inst a
     INNER JOIN Payment
             ON Payment.RegNo = a.RegNo
     WHERE  ABS(a.BaseAmount) BETWEEN ABS(Payment.BaseAmount) - 5
                                  AND ABS(Payment.BaseAmount) + 5
     AND    ISNULL(a.RefInstNo, 0) = 0
     AND    a.InstType <> ''O''
     AND    (CAST(a.StdLedgerID AS VARCHAR(10)) + CAST(a.InstNo AS VARCHAR(1)))
            NOT IN (
                    SELECT CAST(b.RefStdLedgerID AS VARCHAR(10)) +
                              CAST(b.RefInstNo AS VARCHAR(1))
                    FROM   vw_Payment_Ledger_Matching_inst b
                    WHERE  b.RegNo = Payment.RegNo
                    AND    (b.BaseAmount) BETWEEN (Payment.BaseAmount) - 5
                                              AND (Payment.BaseAmount) + 5
                    AND    b.InstType = ''O''
                   )
     ORDER BY a.CenterId, a.RecptKey, a.LedgerDate
)
UPDATE upd
SET    upd.RefInstNo = Ledger.InstNo,
       upd.RefStdLedgerId = Ledger.StdLedgerId,
       upd.RefPayModeId = Ledger.PayModeId,
       upd.RefConcession = Ledger.Concession,
       upd.RefLumpsump = Ledger.LumpSump,
       upd.RefConcessionDtl = Ledger.ConcessionDtl,
       upd.RefLumpsumpDtl = Ledger.LumpsumpDtl
FROM   ' + @UpdateTable + N' upd
INNER JOIN Ledger
        ON Ledger.PaymentStdLedgerId = upd.StdLedgerId
       AND Ledger.PaymentPayModeId = upd.PayModeId
WHERE  Ledger.[RowNumInGroup] = 1; --ensure same behavior as TOP 1 within the CURSOR
';

EXEC (@SQL);
相关问题