Domanda

Ehi, ho un cursore nella stored procedure in SQL Server 2000 (non possibile aggiornare in questo momento) che aggiorna tutti tavolo, ma di solito ci vogliono pochi minuti per essere completata. Ho bisogno di renderlo più veloce. Ecco esempio tabella filtrata da un id prodotto arbitrario; Esempio http://img231.imageshack.us/img231/9464/75187992.jpg Mentre GDEPO: Entry deposito, CDEPO: Exit deposito, Adet:. La quantità, la quantità E_CIKAN che viene utilizzato

spiegazioni Record:
1: 20 unità entra depot 01, 2: 10 unità lascia 01. 3: 5 unità lascia 01 (E_CIKAN per il 1 ° disco sarà ora 15) 4: 10 di più unità entra depot 01. 5: Unità 3 lascia 01 dal 1 ° record. Notate ora primo record è E_CIKAN impostato su 18. 6: questo è dove il problema entra in gioco: 3 unità deve lasciare deposito 01. Ci vogliono 2 unità dal 1 ° disco e 1 unità dal 5 record. Il mio SP grado di gestire questo bene come si vede nella foto, tranne che è molto lento.

Ecco la stored procedure tradotto in inglese;

CREATE PROC [dbo].[UpdateProductDetails]
as
UPDATE PRODUCTDETAILS SET E_CIKAN=0;
DECLARE @ID int
DECLARE @SK varchar(50),@DP varchar(50)  --SK = STOKKODU = PRODUCTID, DP = DEPOT
DECLARE @DEMAND float     --Demand=Quantity, We'll decrease it record by record
DECLARE @SUBID int
DECLARE @SUBQTY float,@SUBCK float,@REMAINS float
DECLARE SH CURSOR FAST_FORWARD FOR
SELECT [ID],PRODUCTID,QTY,EXITDEPOT FROM PRODUCTDETAILS  WHERE (EXITDEPOT IS NOT NULL) ORDER BY [DATE] ASC
OPEN SH
FETCH NEXT FROM SH INTO @ID, @SK,@DEMAND,@DP

WHILE (@@FETCH_STATUS = 0)
BEGIN
   DECLARE SA CURSOR FAST_FORWARD FOR
   SELECT [ID],QTY,E_CIKAN FROM PRODUCTDETAILS  WHERE (QTY>E_CIKAN) AND (PRODUCTID=@SK) AND (ENTRYDEPOT=@DP) ORDER BY [DATE] ASC
   OPEN SA
   FETCH NEXT FROM SA INTO @SUBID, @SUBQTY,@SUBCK
   WHILE (@@FETCH_STATUS = 0) AND (@DEMAND>0)
   BEGIN
      SET @REMAINS=@SUBQTY-@SUBCK
      IF @DEMAND>@REMAINS  --current record isnt sufficient, use it and move on
      BEGIN
         UPDATE PRODUCTDETAILS SET E_CIKAN=QTY WHERE ID=@SUBID;
         SET @DEMAND=@DEMAND-@REMAINS
      END
      ELSE
      BEGIN
         UPDATE PRODUCTDETAILS SET E_CIKAN=E_CIKAN+@DEMAND WHERE ID=@SUBID;
         SET @DEMAND=0
      END
      FETCH NEXT FROM SA INTO @SUBID, @SUBAD,@SUBCK
   END
   CLOSE SA
   DEALLOCATE SA
   FETCH NEXT FROM SH INTO @ID, @SK,@DEMAND,@DP
END
CLOSE SH
DEALLOCATE SH
È stato utile?

Soluzione

In base alla nostra conversazione nella mia altra risposta a questa domanda, penso di aver trovato un modo per accelerare la vostra routine.

Hai due cursori nidificati:

  • La prima è la selezione ogni riga che ha un exitdepot specificato. Ci vuole il prodotto, depo e la quantità, e poi:
  • Il loop cursore interno scorre attraverso le righe di tale prodotto / depot che sono entrydepot specificato. Si aggiunge sul E_CIKAN per ognuno, fino a quando non ha accordato tutto il prodotto.

Così il ciclo cursore interno viene eseguito almeno una volta per ogni riga exitdepot avete. Tuttavia, il sistema in realtà non importa quali elementi è uscito con il quale operazione - si sta solo cercando di calcolare i valori E_CIKAN finali

.

...

Il ciclo esterno ha solo bisogno di ottenere il totale quantità di oggetti spediti fuori per ogni combinazione prodotto / deposito. Quindi si potrebbe cambiare la definizione cursore esterno a:

DECLARE SH CURSOR FAST_FORWARD FOR
    SELECT PRODUCTID,EXITDEPOT, Sum(Qty) as TOTALQTY
    FROM PRODUCTDETAILS  
    WHERE (EXITDEPOT IS NOT NULL) 
    GROUP BY PRODUCTID, EXITDEPOT
OPEN SH
FETCH NEXT FROM SH INTO @SK,@DP,@DEMAND

(e quindi anche modificare l'abbinamento di richiamo da SH alla fine del codice per abbinare, ovviamente)

Questo significa che il cursore esterno avrà molte meno righe a scorrere, e il cursore interno avrà roughtly la stessa quantità di righe da scorrere.

Quindi questo dovrebbe essere più veloce.

Altri suggerimenti

I cursori devono essere la soluzione peggiore di eseguire qualsiasi problema quando si utilizza T-SQL.

Sono disponibili due opzioni a seconda della complessità di quello che stai veramente cercando di realizzare:

  1. tentativo di riscrivere l'intero set di codice per utilizzare le operazioni di set. Questo sarebbe il metodo di esecuzione più veloce ... ma a volte basta non può farlo utilizzando le operazioni di set.

  2. Sostituire il cursore con una combinazione di una variabile di tabella (con colonna identity), contatore, e mentre loop. È quindi possibile scorrere ogni riga della variabile di tabella. Esegue meglio di un cursore ... anche se non può sembrare che sarebbe stato.

Rimuovi il cursore e fare aggiornamenti batch. Devo ancora trovare un aggiornamento che non può essere fatto in batch.

rimuovere il cursore e riscrivere che, come un aggiornamento da entrare nella query del cursore, è possibile effettuare le FI un caso se è necessario. Sono troppo occupato oggi di scrivere l'aggiornamento per voi oggi ...

In primo luogo, se è necessario utilizzare un cursore, e si sta aggiornando roba, quindi dichiarare il cursore con la clausola FOR UPDATE. (Vedere l'esempio di seguito. Si noti che l'esempio non si basa sul vostro codice a tutti.)

Detto questo, ci sono una miriade di modi per utilizzare qualcosa di diverso da cursori, spesso sfruttando le tabelle temporanee. Vorrei indagare su questo percorso in luogo di cursori.

DECLARE LoopingCursor CURSOR LOCAL DYNAMIC
FOR
    select sortorder from customfielddefinition
    where context=@targetContext
FOR UPDATE OF sortorder

Posso capire che il problema che si sta tentando di risolvere è piuttosto complicato:

  • Quando c'è una riga con GDEPO specificato, rappresenta magazzino andare in depo, e si desidera utilizzare l'E_CIKAN di la riga per tenere traccia di quanto dello stock viene utilizzata in seguito . E_CIKAN avrà inizio alle 0 e quindi ottenere aggiunto come magazzino si spegne, fino a raggiungere ADET.

  • Quindi, quando v'è una successiva riga con CDEPO specificato, respresents magazzino di uscire, e si desidera tornare a E_CIKAN del GDEPO fila e regolare l'E_CIKAN, aggiungendo la quantità di brodo-out per esso.

  • Quando ci sono stati due file separate con magazzino andando nella (GDEPO specificato), a volte c'è un overflow quando l'E_CIKAN di una fila raggiunge max (ADET) e poi si desidera aggiungere il resto a quello successivo .

Questo è un calcolo molto difficile perché si deve confrontare diverse righe e tornare indietro e cambiare i valori in forse uno o forse due righe da tenere traccia di ogni operazione di magazzino.

possono essere un modo per farlo senza un cursore, come altri stanno suggerendo. Ma penso che se si potesse riorganizzare le tabelle e memorizzare i dati in un modo diverso, si potrebbe essere in grado di rendere il problema più facile.

Per esempio, invece di tenere traccia del magazzino nella stessa tabella che registra le transazioni di magazzino, potreste avere un tavolo separato con 'Product_id, Depo_id, importo' colonne che tiene traccia della quantità totale di ciascun prodotto in ogni Depo in una sola volta?

Una modifica di progettazione di database come quello potrebbe rendere le cose più facili.

Oppure ... invece di usare E_CIKAN per tenere traccia di ciò che viene utilizzato , usarlo per tenere traccia di quello che rimane . E mantenere un valore E_CIKAN in ogni fila. Così ogni volta che titolo va dentro o fuori di un depo, ri-calcolare E_CIKAN a quel punto nel tempo e conservarlo in quella fila di transazione (invece di cercare di tornare alla originale 'magazzino in' fila e aggiornare lì). Poi, per scoprire lo stock attuale, basta guardare il più recente transcation per quel prodotto / depo.

In sintesi, quello che sto dicendo è, il calcolo è lento e macchinoso, perché si archiviano i dati in un modo strano. Nel lungo periodo potrebbe essere utile cambiare la struttura del database per rendere più facile la programmazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top