0

我正在使用基于 MS SQL Server 的 ERP 系统。它有一个装运提交流程,该流程使用 99 个临时表来执行其工作。这项工作需要是原子的——这一切都需要发生或不应该发生。这个特殊的存储过程会影响数据库中最大的表。编写程序的方式不能很好地扩展。在下午提交大量发货的所有大型客户中,SQL Server 的运行速度显着放缓。当没有负载时,这个过程需要 <1 秒。当许多用户 (15-30) 提交发货时,该过程需要 > 1 分钟。

我有兴趣重写该过程以在负载下提高性能。

所有临时表都有索引和/或聚集索引。

我一直在对过程中使用的永久表周围的索引进行性能调整。

我正在寻找一种更好的架构,它可以保持整个流程的原子性,同时让它更好地扩展。似乎有些临时表将只保存少量记录(< 100),而有些将保存大量记录(> 10,000 但<100,000)(我还没有完成工作来根据经验确定这一点这点)。我看到的替代方案如下:

  • 将一些临时表移动到表变量中以将数据保存在内存中
  • 将部分过程移到从 SQL 调用的基于 CLR 的过程中(也许某些逻辑在过程代码中的执行速度比在 t-sql 中更快)

请让我知道更具体的信息,这将有助于推动这个问题。

附加信息(约翰·普特曼——2013 年 7 月 9 日)

我跟踪执行以查看在此过程的运行中启动和提交的 user_transactions——在处理 SP 时启动和提交了多个事务——大多数都嵌套在调用堆栈中的 SP 中。调用堆栈最多嵌套 9 级(包括触发器时)。大约有 27 个不同的事务被启动和提交。事务不是嵌套的。系统使用逻辑锁表来防止其他用户(进程)在执行存储过程时编辑与“发货”记录相关的记录。

这是存储的主存储过程的片段,以感受代码的味道(注意一些错误记录代码已被删除):

DELETE #tciTransToCommit
FROM #tciTransToCommit WITH (NOLOCK)
JOIN tsoPendShipment ps WITH (NOLOCK) ON #tciTransToCommit.TranKey = ps.ShipKey
WHERE ps.TranType NOT IN (@TRANTYPE_SHIPMENT,
@TRANTYPE_SHIPMENT_TRNSFR,
@TRANTYPE_CUSTOMER_RETURN,
@TRANTYPE_SHIPMENT_DROP_SHIP)
OPTION (KEEPFIXED PLAN)

-- Make sure transactions are still in tsoPendShipment
DELETE #tciTransToCommit
FROM #tciTransToCommit WITH (NOLOCK)
LEFT OUTER JOIN tsoPendShipment ps WITH (NOLOCK) ON #tciTransToCommit.TranKey = ps.ShipKey
WHERE ps.ShipKey IS NULL
OPTION (KEEPFIXED PLAN)

IF NOT EXISTS (SELECT 1 FROM #tciTransToCommit)
BEGIN
-- Nothing to process.  Exit with a success.
SET @oRetVal = @kSuccess
GOTO EarlyExit
END


-- Update the PreCommitBatchKey if it is not set.
UPDATE tmp
SET PreCommitBatchKey = o.ShipmentHiddenBatchKey
FROM #tciTransToCommit tmp WITH (NOLOCK)
JOIN tsoOptions o  WITH (NOLOCK) ON tmp.CompanyID = o.CompanyID
WHERE tmp.TranType IN (@TRANTYPE_SHIPMENT,
@TRANTYPE_SHIPMENT_TRNSFR,
@TRANTYPE_CUSTOMER_RETURN,
@TRANTYPE_SHIPMENT_DROP_SHIP)
AND tmp.PreCommitBatchKey = 0
OPTION (KEEPFIXED PLAN)

    -- Thow away any previously used disposable batches.  Use a new disposable batch every time.  This is done so that
    -- this procedure can properly dispose of the batch without having to rely on the caller to dispose of any batches
    -- used during validations.
    UPDATE tmp
    SET
        DispoBatchKey = NULL
    FROM
        #tciTransToCommit tmp WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

-- Make sure the rows in #tciTransToCommit are unique rows.
INSERT @UniqueTransToCommit (CompanyID, TranType, PostDate, InvcDate, TranKey, PreCommitBatchKey, DispoBatchKey, CommitStatus)
SELECT DISTINCT CompanyID, TranType, PostDate, InvcDate, TranKey, PreCommitBatchKey, DispoBatchKey, CommitStatus
FROM #tciTransToCommit WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

TRUNCATE TABLE #tciTransToCommit
INSERT #tciTransToCommit (CompanyID, TranType, PostDate, InvcDate, TranKey, PreCommitBatchKey, DispoBatchKey, CommitStatus)
SELECT CompanyID, TranType, PostDate, InvcDate, TranKey, PreCommitBatchKey, DispoBatchKey, CommitStatus
FROM @UniqueTransToCommit
OPTION (KEEPFIXED PLAN)

    ---------------------------------------------------------------------------------
    -- Create the disposable batches based on the records found in #tciTransToCommit.
    -- Note that a new disposable batch will be generated for each run.
    ---------------------------------------------------------------------------------

-- Reset RetVal to zero.
SELECT @lRetVal = 0
EXEC spciCreateInvtCommitDisposableBatch @lRetVal OUTPUT
IF @lRetVal <> @kSuccess
GOTO ErrorOccurred


---------------------------------------------------------------------------
-- Use one SessionID for all disposable batches we are about to commit to
-- assist in reporting.
---------------------------------------------------------------------------

IF COALESCE(@oSessionID, 0) <> 0
BEGIN
-- Use the SessionID passed in.
SELECT @lSessionID = @oSessionID
END
ELSE
BEGIN
-- Select the BatchKey of the first disposable batch to act as the SessionID for all batches being processed.
SELECT @lSessionID = MIN(DispoBatchKey) FROM #tciTransToCommit WITH (NOLOCK)
SELECT @oSessionID = @lSessionID
END

-- Clear the error tables and the logical lock table.  Only clear the errors if they are for an existing batch.
-- Credit card processing may have already populated errors that we don't want to clear -- the batch log won't
-- exist for this session ID
DELETE tciErrorLog
FROM   tciErrorLog
JOIN   tciBatchLog
ON     tciErrorLog.SessionID = tciBatchLog.BatchKey
WHERE SessionID = @lSessionID
OPTION (KEEPFIXED PLAN)

TRUNCATE TABLE #tciErrorLogExt

    -----------------------------------------------------------------------------
    -- Logical lock cleanup.
    -----------------------------------------------------------------------------
    EXEC spsmLogicalLockCleanup
        @LOCKTYPE_SO_BATCH_RECOVERY
        ,@lRetVal
        ,1


IF @lRecTimeFlag = 1
BEGIN
SELECT GETDATE() - @lProcTime 'CreateDispoBatch'
SELECT @lProcTime = GETDATE()
END


-- The following section needs a clean #LogicalLocks temp table.  The locks that were inserted above have be set aside in another temp table
-- and they will be deleted manually.
TRUNCATE TABLE #LogicalLocks

-- Do not create a logical lock when we are only validating the data.
IF @iOptValidateOnly = 0
BEGIN
-- ------------------------------------------------------
-- Create Logical Locks against the selected transaction:
-- ------------------------------------------------------

-- Place a logical lock on the transactions found in #tciTransToCommit so other
-- processes trying to commit the same transactions will get an exclusive lock error.
-- Only attempt to lock records that will be processed.
INSERT #LogicalLocks
(LogicalLockType,
UserKey,
LockType,
LogicalLockID)
SELECT
1,
TranKey,
@LOCK_MODE_EXCLUSIVE,
'SOCommitTrnxs: ' + CONVERT(VARCHAR(10), TranType) + ': ' + CONVERT(VARCHAR(10), TranKey)
FROM #tciTransToCommit WITH (NOLOCK)
      WHERE CommitStatus IN (@COMMIT_STATUS_DEFAULT, @PRE_COMMIT_STATUS_SUCCESS, @COMMIT_STATUS_WARNINGS_EXISTS)
OPTION (KEEPFIXED PLAN)

-- Get the users login time
SELECT @SPIDLoginTime = login_time FROM master..sysprocesses WITH (NOLOCK) WHERE spid = @@SPID

-- If the Credit Card module is activated, it is possible that logical locks were created
-- during authorization.  Do not re-create the same locks if this SP is called by the same
-- connection where the lock type (shared/exclusive) matches.  Do this by updating the
-- LogicalLockKey and setting the Status column to 1.  The locking SP will skip these records
-- because it thinks locks have already been creatd.  Use the SPID and login time
-- to tell if this SP is called by the same connection.
UPDATE #LogicalLocks
SET LogicalLockKey = ll.LogicalLockKey,
[Status] = 1 -- Lock created.  In this case already created.
FROM #LogicalLocks
JOIN tsmLogicalLock ll WITH (NOLOCK)
ON #LogicalLocks.LogicalLockID = ll.LogicalLockID
AND #LogicalLocks.LogicalLockType = ll.LogicalLockType
AND #LogicalLocks.LockType = ll.LockType
AND ll.SPID = @@SPID AND ll.LoginTime = @SPIDLoginTime


EXEC spsmLogicalLockAddMultiple @lRetVal OUTPUT, @lLocksCreated OUTPUT, @lLocksRejected OUTPUT, @CLEANUP_LOCKS_FIRST
IF (@lRetVal NOT IN (1,2))
BEGIN

--------------------------------------------------------------------------
-- At least one logical lock could not be obtained for the transactions to
-- be comitted.  Log an error message and exit with success so that this
-- can be reported back to the user.  Do not worry about @lLocksRejected
-- here.  The next code block will report individual locks back to the
-- user.
--------------------------------------------------------------------------

-- At least one logical lock could not be obtained for the transactions.
INSERT #tciError
(EntryNo, BatchKey, StringNo,
StringData1, StringData2, StringData3,
ErrorType, Severity, TranType,
TranKey, InvtTranKey)
VALUES(
NULL,@lSessionID, 250981,
'', '', '',
2, @FATAL_ERR, NULL,
NULL, NULL)

EXEC spciLogErrors @lSessionID, @lRetVal OUTPUT, @lSessionID

SELECT @oRetVal = @kSuccess
GOTO EarlyExit

END

-- See if the locks were successfully placed for the transactions.
SELECT 1 FROM #LogicalLocks tmpll WITH (NOLOCK) JOIN #tciTransToCommit tmp ON tmpll.UserKey = tmp.TranKey WHERE tmpll.Status <> 1 OPTION (KEEPFIXED PLAN)
IF @@ROWCOUNT <> 0
BEGIN
-- Tran {0}: User {1} currently has a lock against this transaction.
INSERT #tciError
(EntryNo, BatchKey, StringNo,
StringData1, StringData2, StringData3,
ErrorType, Severity, TranType,
TranKey, InvtTranKey)
SELECT
NULL, tmp.DispoBatchKey, 100524,
ps.TranID, ll.ActualUserID, '',
2, @FATAL_ERR, tmp.TranType,
tmp.TranKey, NULL
FROM tsmLogicalLock ll WITH (NOLOCK)
JOIN #LogicalLocks tmpll WITH (NOLOCK) ON ll.LogicalLockID = tmpll.LogicalLockID AND ll.LogicalLockType = tmpll.LogicalLockType
JOIN #tciTransToCommit tmp WITH (NOLOCK) ON tmpll.UserKey = tmp.TranKey
JOIN tsoPendShipment ps WITH (NOLOCK) ON tmp.TranKey = ps.ShipKey
WHERE tmpll.Status = 102 -- Exclusive lock not created due to existing locks.
OPTION (KEEPFIXED PLAN)

-- Tran {0}: User {1} currently has a lock against this transaction.
INSERT #tciError
(EntryNo, BatchKey, StringNo,
StringData1, StringData2, StringData3,
ErrorType, Severity, TranType,
TranKey, InvtTranKey)
SELECT
NULL, tmp.DispoBatchKey, 100524,
ps.TranID, ll.ActualUserID, '',
2, @FATAL_ERR, tmp.TranType,
tmp.TranKey, NULL
FROM tsmLogicalLock ll WITH (NOLOCK)
JOIN #LogicalLocks tmpll WITH (NOLOCK) ON ll.LogicalLockID = tmpll.LogicalLockID AND ll.LogicalLockType = tmpll.LogicalLockType
JOIN #tciTransToCommit tmp WITH (NOLOCK) ON tmpll.UserKey = tmp.TranKey
JOIN tsoPendShipment ps WITH (NOLOCK) ON tmp.TranKey = ps.ShipKey
WHERE tmpll.Status NOT IN (1, 102) -- NOT(Locked Successfully, Exclusive lock not created due to existing locks)
OPTION (KEEPFIXED PLAN)

-- Mark those transactions which locks could not be created.  This will exclude
-- them from the list of transactions to be processed.
UPDATE tmp
SET CommitStatus = @COMMIT_STATUS_TRAN_LOCKED
FROM #tciTransToCommit tmp WITH (NOLOCK)
JOIN #LogicalLocks ll WITH (NOLOCK) ON tmp.TranKey = ll.UserKey
WHERE ll.Status <> 1 -- Not Locked Successfully
OPTION (KEEPFIXED PLAN)

...Error logging code removed....


---------------------------------------------------------------------------
-- Create a list of batches to be committed.  Each row in @UniqueBatchType
-- will require a call to the SO Posting Routines.  This table drives the
-- WHILE loops within the routine. Included the @PRE_COMMIT_STATUS_SUCCESS
-- and @COMMIT_STATUS_WARNINGS_EXISTS statuses since this routine may have
-- been called a 2nd time.
---------------------------------------------------------------------------
INSERT @UniqueBatchType (CompanyID, BatchType, DispoBatchKey)
SELECT DISTINCT tmp.CompanyID, bl.BatchType, tmp.DispoBatchKey
FROM #tciTransToCommit tmp WITH (NOLOCK)
JOIN tciBatchLog bl WITH (NOLOCK) ON tmp.DispoBatchKey = bl.BatchKey
WHERE bl.PostStatus = 0 AND bl.Status = 4 -- Post status is Opened and Balanced.
AND tmp.CommitStatus IN (@COMMIT_STATUS_DEFAULT, @PRE_COMMIT_STATUS_SUCCESS, @COMMIT_STATUS_WARNINGS_EXISTS)
OPTION (KEEPFIXED PLAN)

IF @@ERROR <> 0
GOTO ErrorOccurred

---------------------------------------------------------------------------
-- Check if there is anything to process.  Reasons why @UniqueBatchType
-- could be empty:
--      > Could not create a Logical Lock on ALL of the transactions.
--      > Being called a 2nd time but ALL of the transactions have at least
--        one fatal error.
---------------------------------------------------------------------------

SELECT 1 FROM @UniqueBatchType
IF @@ROWCOUNT = 0
BEGIN
--R--
SELECT @oRetVal = @kSuccess
GOTO EarlyExit
END

-----------------------------------------------------
-- Create logical locks against the disposable batch.
-----------------------------------------------------

INSERT #LogicalLocks
(LogicalLockType,
UserKey,
LockType,
LogicalLockID,
LockCleanupParam1,
LockCleanupParam2,
LockCleanupParam3)
SELECT
@LOCKTYPE_SO_BATCH_RECOVERY,
DispoBatchKey,
@LOCK_MODE_EXCLUSIVE,
'DISPOBATCHKEY: ' + CONVERT(VARCHAR(10), DispoBatchKey),
DispoBatchKey,
@oSessionID,
CompanyID
FROM @UniqueBatchType
WHERE BatchType IN (@BATCH_TYPE_SHIPMENTS, @BATCH_TYPE_RETURNS) AND COALESCE(DispoBatchKey, 0) <> 0

EXEC spsmLogicalLockAddMultiple @lRetVal OUTPUT, @lLocksCreated OUTPUT, @lLocksRejected OUTPUT, @DO_NOT_CLEANUP_LOCKS_FIRST
IF @lRetVal NOT IN (1,2)
GOTO ErrorOccurred

-- See if the locks were successfully placed for the Disposable Batch(es).
SELECT 1 FROM #LogicalLocks tmpll WITH (NOLOCK) JOIN @UniqueBatchType tmp ON tmpll.UserKey = tmp.DispoBatchKey WHERE tmpll.Status <> 1 OPTION (KEEPFIXED PLAN)
IF @@ROWCOUNT <> 0
BEGIN
-- {0}{1} Unable to Lock the Batch.
INSERT #tciError
(EntryNo, BatchKey, StringNo,
StringData1, StringData2, StringData3,
ErrorType, Severity, TranType,
TranKey, InvtTranKey)
SELECT
NULL, tmp.DispoBatchKey, 150656,
'', '', '',
2, @FATAL_ERR, NULL,
NULL, NULL
FROM tsmLogicalLock ll WITH (NOLOCK)
JOIN #LogicalLocks tmpll WITH (NOLOCK) ON ll.LogicalLockID = tmpll.LogicalLockID AND ll.LogicalLockType = tmpll.LogicalLockType
JOIN @UniqueBatchType tmp ON tmpll.UserKey = tmp.DispoBatchKey
WHERE tmpll.Status NOT IN (1, 102) -- NOT(Locked Successfully, Exclusive lock not created due to existing locks)
OPTION (KEEPFIXED PLAN)

-- If we could not create a logical lock against the disposable batch, do not continue.
IF @@ROWCOUNT <> 0
BEGIN
EXEC spciLogErrors @lSessionID, @lRetVal OUTPUT, @lSessionID
GOTO ErrorOccurred
END
END

-- -------------------------
-- Start Pre-Commit Routine:
-- -------------------------

-- The spsmLogicalLockRemoveMultiple does an inner join to tsmLogicalLock on the LogicalLockKey
-- to delete the lock.  I want to set the logical lock keys to a negative number so we can control
-- which locks to remove.  They will be set to a positive before the call to remove the locks.
UPDATE #LogicalLocks
SET LogicalLockKey = ABS(LogicalLockKey) * -1
FROM #LogicalLocks WITH (NOLOCK)
JOIN #tciTransToCommit tmp WITH (NOLOCK) ON #LogicalLocks.UserKey = tmp.TranKey
WHERE #LogicalLocks.Status = 1 -- Locked Successfully
OPTION (KEEPFIXED PLAN)

IF @lRecTimeFlag = 1
BEGIN
SELECT GETDATE() - @lProcTime 'LogicalLocks'
SELECT @lProcTime = GETDATE()
END

IF @lDebugFlag = 1
SELECT '#tciTransToCommit Before Pre-Commit', * FROM #tciTransToCommit WITH (NOLOCK)

SELECT @lBatchCount = MIN(BatchCount) FROM @UniqueBatchType

TRUNCATE TABLE #tglPostingDetlTran
TRUNCATE TABLE #tglPostingRpt
TRUNCATE TABLE #timPostingHolding
TRUNCATE TABLE #tarAPIValidHolding
TRUNCATE TABLE #tarAPIPendInvcHolding
TRUNCATE TABLE #tarAPIPendInvcAmtsHolding
TRUNCATE TABLE #tarAPIInvcDetlHolding
TRUNCATE TABLE #tarAPICmntOnlyHolding
TRUNCATE TABLE #tarSalesCommHolding
TRUNCATE TABLE #tciSTaxCodeTranHolding
TRUNCATE TABLE #tciSTaxTranHolding

IF COALESCE(@lBatchCount, 0) > 0
BEGIN

-- Loop through @UniqueBatchType and call the commit disposable batch routines.
WHILE @lBatchCount IS NOT NULL
BEGIN

SELECT @lCompanyID = CompanyID, @lDispoBatchKey = DispoBatchKey, @lBatchType = BatchType
FROM @UniqueBatchType WHERE BatchCount = @lBatchCount

-- Assign the transactions to be committed to the disposable batch.
UPDATE ps
SET BatchKey = tmp.DispoBatchKey,
UpdateDate = GETDATE(), UpdateUserID = @lLoginName
FROM tsoPendShipment ps WITH (NOLOCK)
JOIN #tciTransToCommit tmp WITH (NOLOCK) ON ps.ShipKey = tmp.TranKey
JOIN @UniqueBatchType bt ON tmp.DispoBatchKey = bt.DispoBatchKey
WHERE tmp.CommitStatus IN (@COMMIT_STATUS_DEFAULT,
@PRE_COMMIT_STATUS_SUCCESS,
@COMMIT_STATUS_WARNINGS_EXISTS)
AND bt.DispoBatchKey = @lDispoBatchKey
OPTION (KEEPFIXED PLAN)

IF @@ERROR <> 0
GOTO ErrorOccurred

-- Reset RetVal to zero.
SELECT @lPreCommitRetVal = 0
-- Call the routine to pre-commit the batches.  This is basically the old SO pre-posting routines.
EXEC spsoPreCommitDisposableBatch @lSessionID, @lDispoBatchKey, @iOptValidateOnly, @iOptSkipValidation, @lPreCommitRetVal OUTPUT

-- Store off the records in #timPosting into #timPostingHolding for the current BatchKey.
-- We will reload #timPosting before calling spsoCommitDisposableBatch.
-- This must happen before checking @lPreCommitRetVal because UndoEverything relies on
-- #timPostingHolding being populated.
INSERT #timPostingHolding
SELECT DISTINCT * FROM #timPosting p WITH (NOLOCK)
WHERE p.BatchKey = @lDispoBatchKey
AND NOT EXISTS (SELECT 1 FROM #timPostingHolding hold WITH (NOLOCK)
WHERE (p.InvtTranKey IS NOT NULL AND p.InvtTranKey = hold.InvtTranKey) )
OPTION (KEEPFIXED PLAN)

IF @lPreCommitRetVal <> @kSuccess
...Error logging code removed....

-- Store off the records in tglPosting into #tglPostingRpt for the current BatchKey.
-- We will always use the #tglPostingRpt table to report the GL Register.
INSERT #tglPostingRpt (
AcctRefKey, BatchKey, CurrID, ExtCmnt, GLAcctKey, JrnlKey, JrnlNo, NatCurrBegBal, PostAmt, PostAmtHC,
PostCmnt, PostDate, PostQty, SourceModuleNo, Summarize, TranDate, TranKey, TranNo, TranType)
SELECT
AcctRefKey, BatchKey, CurrID, ExtCmnt, GLAcctKey, JrnlKey, JrnlNo, NatCurrBegBal, PostAmt, PostAmtHC,
PostCmnt, PostDate, PostQty, SourceModuleNo, Summarize, TranDate, TranKey, TranNo, TranType
FROM tglPosting WITH (NOLOCK) WHERE BatchKey = @lDispoBatchKey
OPTION (KEEPFIXED PLAN)

IF @@ROWCOUNT > 0
BEGIN
INSERT #tglPostingDetlTran (PostingDetlTranKey, TranType)
SELECT DISTINCT sl.InvtTranKey, s.TranType
FROM #tciTransToCommit tmp WITH (NOLOCK)
JOIN tsoPendShipment s  WITH (NOLOCK) ON tmp.TranKey = s.ShipKey
JOIN tsoShipLine sl WITH (NOLOCK) ON s.ShipKey = sl.ShipKey
JOIN #tglPostingRPT gl  WITH (NOLOCK) ON sl.InvtTranKey = gl.TranKey AND s.TranType = gl.TranType
WHERE tmp.CommitStatus IN (@COMMIT_STATUS_WARNINGS_EXISTS, @PRE_COMMIT_STATUS_SUCCESS)
AND tmp.TranType IN (@TRANTYPE_SHIPMENT, @TRANTYPE_SHIPMENT_TRNSFR, @TRANTYPE_CUSTOMER_RETURN)
AND gl.BatchKey = @lDispoBatchKey
OPTION (KEEPFIXED PLAN)

EXEC spglSummarizeBatchlessTglPosting @lCompanyID, @lDispoBatchKey, @lRetVal OUTPUT, 1 -- Indicate to use temp table (#tglPostingRPT)
END

INSERT #tarAPIValidHolding (AcctRefUsage,AcuityUserID,ApplyToInvcKey,BatchID,BatchKey,BatchOvrdSales,BatchOvrdSegKey,BatchOvrdSegVal,BatchType,BillToAddrKey,BillToAddrLine1,BillToAddrLine2,BillToAddrLine3,BillToAddrLine4,BillToAddrLine5,BillToAddrName,BillToCity,BillToCountry,BillToCustAddrKey,BillToPostalCode,BillToState,CheckCredit,ClassOvrdSales,ClassOvrdSegKey,ClassOvrdSegVal,CommPlanKey,CompanyID,ContactKey,CreateGL,CurrExchRate,CurrExchSchdKey,CurrID,CustAddrKey,CustClassKey,CustID,CustKey,CustPONo,CustSalesAcctKey,DocCurrID,DownPmtCustPmtKey,FOBKey,HomeCurrID,HomeRoundAmt,ImportLogKey,InclTradeDiscInSls,InputTranNo,InvcCommPlanKey,InvcFormKey,InvcKey,LastRetVal,LockID,LogSuccessful,NextEntryNo,NextSeqNo,PmtTermsKey,PriceListKey,PrimarySperKey,Printed,PrintInvcs,ReasonCodeKey,RecordNumber,ReferenceID,RoundCost,RoundDocAmt,RoundPrice,RoundQty,SameNoRangeForMemo,SeqNo,ShipAmt,ShipMethKey,ShipToAddrKey,ShipToAddrLine1,ShipToAddrLine2,ShipToAddrLine3,ShipToAddrLine4,ShipToAddrLine5,ShipToAddrName,ShipToCity,ShipToCountry,ShipToCustAddrKey,ShipToPostalCode,ShipToState,ShipZoneKey,SourceModule,Spid,STaxSchdKey,TrackSTaxOnSales,TradeDiscPct,TranCmnt,TranDate,TranID,TranNo,TranStatus,TranType,UniqueID,UseMultCurr,UseSper,DispoBatchKey)
SELECT AcctRefUsage,AcuityUserID,ApplyToInvcKey,BatchID,BatchKey,BatchOvrdSales,BatchOvrdSegKey,BatchOvrdSegVal,BatchType,BillToAddrKey,BillToAddrLine1,BillToAddrLine2,BillToAddrLine3,BillToAddrLine4,BillToAddrLine5,BillToAddrName,BillToCity,BillToCountry,BillToCustAddrKey,BillToPostalCode,BillToState,CheckCredit,ClassOvrdSales,ClassOvrdSegKey,ClassOvrdSegVal,CommPlanKey,CompanyID,ContactKey,CreateGL,CurrExchRate,CurrExchSchdKey,CurrID,CustAddrKey,CustClassKey,CustID,CustKey,CustPONo,CustSalesAcctKey,DocCurrID,DownPmtCustPmtKey,FOBKey,HomeCurrID,HomeRoundAmt,ImportLogKey,InclTradeDiscInSls,InputTranNo,InvcCommPlanKey,InvcFormKey,InvcKey,LastRetVal,LockID,LogSuccessful,NextEntryNo,NextSeqNo,PmtTermsKey,PriceListKey,PrimarySperKey,Printed,PrintInvcs,ReasonCodeKey,RecordNumber,ReferenceID,RoundCost,RoundDocAmt,RoundPrice,RoundQty,SameNoRangeForMemo,SeqNo,ShipAmt,ShipMethKey,ShipToAddrKey,ShipToAddrLine1,ShipToAddrLine2,ShipToAddrLine3,ShipToAddrLine4,ShipToAddrLine5,ShipToAddrName,ShipToCity,ShipToCountry,ShipToCustAddrKey,ShipToPostalCode,ShipToState,ShipZoneKey,SourceModule,Spid,STaxSchdKey,TrackSTaxOnSales,TradeDiscPct,TranCmnt,TranDate,TranID,TranNo,TranStatus,TranType,UniqueID,UseMultCurr,UseSper,@lDispoBatchKey
FROM #tarAPIValid WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

INSERT #tarAPIPendInvcHolding (ApplyToInvcKey,BillToAddrKey,BillToAddrLine1,BillToAddrLine2,BillToAddrLine3,BillToAddrLine4,BillToAddrLine5,BillToAddrName,BillToCity,BillToCopyKey,BillToCountry,BillToCustAddrKey,BillToPostalCode,BillToState,CommPlanKey,ConfirmToCntctKey,CurrExchRate,CurrExchSchdKey,CurrID,CustClassKey,CustKey,CustPONo,...<whole bunch more fields..>)
SELECT ApplyToInvcKey,BillToAddrKey,BillToAddrLine1,BillToAddrLine2,BillToAddrLine3,BillToAddrLine4,BillToAddrLine5,BillToAddrName,BillToCity,BillToCopyKey,BillToCountry,BillToCustAddrKey,BillToPostalCode,BillToState,CommPlanKey,ConfirmToCntctKey,CurrExchRate,CurrExchSchdKey,CurrID,CustClassKey,CustKey,CustPONo,...<whole bunch more fields..>
FROM #tarAPIPendInvc WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

INSERT #tarAPIPendInvcAmtsHolding (ActCommAmt,CalcCommAmt,CostOfSales,CreditHold,DigitsNC,DiscAmt,DiscDate,DocumentNo,DueDate,InvcKey,SalesAmt,SalesAmtHC,SeqNo,ShipAmt,ShipAmtHC,STaxAmt,STaxAmtHC,STaxTranKey,TradeDiscAmt,TradeDiscAmtHC,TranAmt,TranAmtHC,DispoBatchKey)
SELECT ActCommAmt,CalcCommAmt,CostOfSales,CreditHold,DigitsNC,DiscAmt,DiscDate,DocumentNo,DueDate,InvcKey,SalesAmt,SalesAmtHC,SeqNo,ShipAmt,ShipAmtHC,STaxAmt,STaxAmtHC,STaxTranKey,TradeDiscAmt,TradeDiscAmtHC,TranAmt,TranAmtHC,@lDispoBatchKey
FROM #tarAPIPendInvcAmts WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

INSERT #tarAPIInvcDetlHolding (AcctRefKey,ActCommAmt,BTOComponent,CalcComm,CalcCommAmt,CmntOnly,CommBase,CommClassKey,CommPlanKey,Description,DetailKey,DetailNo,DocumentNo,...<whole bunch more fields..>)
SELECT AcctRefKey,ActCommAmt,BTOComponent,CalcComm,CalcCommAmt,CmntOnly,CommBase,CommClassKey,CommPlanKey,Description,DetailKey,DetailNo,DocumentNo,...<whole bunch more fields..>
FROM #tarAPIInvcDetl WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

INSERT #tarAPICmntOnlyHolding (AcctRefKey,ActCommAmt,BTOComponent,CalcComm,CalcCommAmt,CmntOnly,CommBase,CommClassKey,CommPlanKey,Description,DetailKey,DetailNo,DocumentNo,...<whole bunch more fields..>)
SELECT AcctRefKey,ActCommAmt,BTOComponent,CalcComm,CalcCommAmt,CmntOnly,CommBase,CommClassKey,CommPlanKey,Description,DetailKey,DetailNo,DocumentNo,...<whole bunch more fields..>
FROM #tarAPICmntOnly WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

INSERT #tarSalesCommHolding (ActCommAmt,BatchKey,CalcCommAmt,CommPaidAmt,CommType,DocumentNo,EditCommAmt,OvrdUserID,SalesCommKey,Selected,SperKey,Status,SubjCOS,SubjSales,DispoBatchKey)
SELECT ActCommAmt,BatchKey,CalcCommAmt,CommPaidAmt,CommType,DocumentNo,EditCommAmt,OvrdUserID,SalesCommKey,Selected,SperKey,Status,SubjCOS,SubjSales,@lDispoBatchKey
FROM #tarSalesComm WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

INSERT #tciSTaxCodeTranHolding (ActNonRecoverAmt,ActSTaxAmt,ActUseTaxAmt,CalcNonRecoverAmt,CalcSTaxAmt,CalcUseTaxAmt,ExmptAmt,STaxCodeKey,STaxExmptNo,STaxTranKey,SubjFreightAmt,SubjSales,SubjSTaxAmt,DispoBatchKey)
SELECT ActNonRecoverAmt,ActSTaxAmt,ActUseTaxAmt,CalcNonRecoverAmt,CalcSTaxAmt,CalcUseTaxAmt,ExmptAmt,STaxCodeKey,STaxExmptNo,STaxTranKey,SubjFreightAmt,SubjSales,SubjSTaxAmt,@lDispoBatchKey
FROM #tciSTaxCodeTran WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

INSERT #tciSTaxTranHolding (STaxTranKey, STaxSchdKey, NeedInsert, DispoBatchKey)
SELECT STaxTranKey, STaxSchdKey, NeedInsert,@lDispoBatchKey
FROM #tciSTaxTran WITH (NOLOCK)
OPTION (KEEPFIXED PLAN)

-- Set CommitStatus to indicate preprocess completed successfully.
UPDATE #tciTransToCommit
SET CommitStatus = @PRE_COMMIT_STATUS_SUCCESS
WHERE DispoBatchKey = @lDispoBatchKey
OPTION (KEEPFIXED PLAN)
4

1 回答 1

1

当你需要“原子”时,你需要 TRANS(BEGIN TRAN,COMMIT TRAN)。

在 BEGIN TRAN 语句之前将所有的“粉碎”或“解析”到您的#temp 表中。

Aka,在调用 BEGIN TRAN 之前,应该设置所有的多米诺骨牌。Aka,BEGIN TRAN/COMMIT TRAN 中的所有内容都应该尽可能精简。

查看 Tran 中 Insert/Update/(Merge/Upsert)/Delete Statements 的顺序。确保它们有意义....并特别注意会导致另一个通常称为存储过程的阻塞问题(死锁问题)。

…………

谷歌了

sql server 临时数据库最佳实践

这是一个启动器:

http://msdn.microsoft.com/en-us/library/ms175527(v=sql.105).aspx

…………

您可以使用@variable 表“试验”,但这是一个成功或失败的提议。

于 2013-07-09T17:45:13.007 回答