我在表中有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();
}
}
}
}
答案 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);