Perché la gente odia i cursori di SQL così tanto?

Posso capire che vogliono evitare di dover utilizzare un cursore a causa del sovraccarico e disagi, ma sembra che ci sia qualche grave cursore-fobia-mania di andare dove la gente sta andando alla grande lunghezza per evitare di dover utilizzare uno.

Per esempio, una domanda ha chiesto come fare qualcosa, ovviamente banale con un cursore e accettato risposta suggerito l’uso di una espressione di tabella comune) query ricorsiva con un ricorsiva della funzione personalizzata, anche se questo limita il numero di righe che possono essere trattati a 32 (a causa della chiamata di funzione ricorsiva limite in sql server). Questa mi suona come una terribile soluzione per la durata del sistema, per non parlare di un enorme sforzo per evitare di usare un semplice cursore.

Qual è il motivo di questo livello di folle odio? Ha un po ‘notato autorità ha emesso una fatwa contro i cursori? Fa un po ‘ di indicibile male in agguato nel cuore di cursori che corrompe i costumi dei bambini o qualcosa del genere?

Wiki domanda, interessa di più la risposta di rep.

Relative Info:

SQL Server Fast Forward Cursori

EDIT: mi permetta di essere più preciso: ho capito che i cursori non deve essere usato al posto delle normali operazioni relazionali; che è un no-brainer. Quello che non capisco è la gente che va waaaaay fuori del loro modo di evitare i cursori come hanno pidocchi o qualcosa del genere, anche quando il cursore è un modo più semplice e/o soluzione più efficiente. È irrazionale odio che elude, non evidenti incrementi di efficienza tecnica.

  • Penso che la tua Modifica dice tutto… In quasi tutte le situazioni (che ho incontrato) c’è un modo per sostituire un cursore con un rendimento migliore in base alla situazione. Dire di no-brainer, ma si capisce il diff.
  • Io amo i tag su questa domanda!
  • La parte sulla CTE ricorsiva limiti 32 è una sciocchezza. Presumibilmente si sta pensando di trigger ricorsivi e il max @@NESTLEVEL di 32. Si può impostare la query con OPTION (MAXRECURSION N) con default 100 e 0 significato illimitato.
  • il limite predefinito è ora 100, e il massimo è di 32 kb sql-server-helper.com/error-messages/msg-310.aspx
  • No, no, è ancora esattamente la stessa di quando ho fatto il mio commento e in tutte le versioni di SQL Server che supportano la Cte ricorsiva. Come il tuo link dice “Quando è specificato 0, non viene applicato alcun limite.”
  • grazie, il mio errore – due errori, in realtà 😉 il primo è stato il fraintendimento di riferimento (ho assunto 32K limite = ‘illimitata’) e il secondo è stato la causa sbagliata – nell’esempio citato, la ricorsione limite di 32 venuto dalla funzione ricorsiva, non CTE. Probabilmente ero l’utilizzo di SQL Server 2000, o forse 2008, al momento, speriamo che sia meglio ora :). Domanda a cura di chiarire – apprezzo la tua correzione!



14 Replies
  1. 73

    Il “sovraccarico” con i cursori è solamente una parte delle API. I cursori sono come parti di RDBMS lavoro sotto il cofano. Spesso CREATE TABLE e INSERT hanno SELECT dichiarazioni, e la sua realizzazione è ovvio interno cursore attuazione.

    Uso di livello superiore “basati su un insieme di operatori di” fagottini il cursore risultati in un singolo set, il che significa meno API di back-e-indietro.

    Cursori anteriori lingue moderne che offrono una prima classe di collezioni. Vecchia C, COBOL, Fortran, ecc…. doveva elaborare le righe, uno alla volta, perché non c’era il concetto di “collezione”, che potrebbero essere ampiamente utilizzato. Java, C#, Python, ecc…. sono di prima classe della lista di strutture per contenere il set di risultati.

    Slow Problema

    In alcuni ambienti, la join relazionali sono un mistero, e la gente scriverà cursori nidificati, piuttosto che una semplice join. Ho visto veramente epico ciclo nidificato operazioni scritto come un sacco e un sacco di cursori. Sconfiggere un RDBMS ottimizzazione. E in esecuzione molto lentamente.

    Semplice SQL riscrive per sostituire il cursore nidificata loop join e una singola, appartamento cursore loop può eseguire i programmi in 100 il tempo. [Pensavano che fossi il dio di ottimizzazione. Tutto quello che ho fatto è stato sostituire nested loop join. Ancora usate i cursori.]

    Questa confusione porta spesso a un atto d’accusa contro i cursori. Tuttavia, non è il cursore, è l’uso improprio del cursore che è il problema.

    Il Problema Della Dimensione

    Per davvero epico set di risultati (cioè, il dumping una tabella in un file), i cursori sono essenziali. Il set base di operazioni non possono materializzarsi davvero grandi insiemi di risultati come un unico insieme in memoria.

    Alternative

    Cerco di usare un ORM livello, per quanto possibile. Ma che ha due scopi. Primo, i cursori sono gestiti da ORM componente. Secondo, l’SQL è separato dall’applicazione in un file di configurazione. Non è che i cursori sono male. È che la codifica di tutti coloro che si apre, si chiude e si recupera non è un valore aggiunto di programmazione.

    • “I cursori sono come RDBMS opere sotto il cofano.” Se ti riferisci in particolare SQL Server, OK, bene, io sono ignorante di che. Ma ho lavorato all’interno di più RDBMS (e ORDBMS) (sotto Stonebraker) e nessuno di loro abbia fatto. Ad esempio: Ingres utilizza ciò che equivale a “set di risultati” di tuple internamente.
    • T: sto lavorando off di seconda mano informazioni su RDBMS origine; potrai modificare la dichiarazione.
    • “Ho visto veramente epico ciclo nidificato operazioni scritto come un sacco e un sacco di cursori.” Continuo a vedere troppo. È difficile credere.
  2. 41

    Cursori rendere le persone eccessivamente applicare un procedurali mentalità di un set di base di ambiente.

    E sono LENTO!!!

    Da SQLTeam:

    Si prega di notare che i cursori sono
    Modo più LENTO per accedere ai dati all’interno di SQL
    Server. Il deve essere utilizzato solo quando
    avete davvero bisogno di accedere a una riga alla
    tempo. L’unico motivo che posso pensare
    per che è quello di chiamare una stored procedure
    su ogni riga. Nel Cursore
    Prestazioni articolo
    ho scoperto
    che i cursori sono più di trenta volte
    inferiore a set alternative a base di
    .

    • che l’articolo è di 7 anni, pensi che forse le cose sono cambiate nel frattempo?
    • Penso anche che i cursori sono molto lento e deve essere evitato, in generale. Tuttavia, se l’OP era riferito alla domanda penso che lui è stato, quindi, un cursore era la soluzione corretta non c’ (lo streaming di un record alla volta a causa di problemi di memoria).
    • aggiornato l’articolo non è corretto, la velocità relativa di misura, ma fornisce alcune buone ottimizzazioni e alternative. Si noti che l’articolo originale dice che i cursori sono 50 volte più veloce di cicli while, che è interessante
    • Io personalmente penso che se avete bisogno di un cursore non hai progettato il database correttamente, in primo luogo.
    • Io personalmente penso che se si fanno coperta affermazioni come che si può davvero essere di 45 anni 😛
    • I cursori non sono così lenta nel Oracle, dove è possibile utilizzare una clausola del tipo MASSA-di RACCOGLIERE per le prestazioni-saggio di codice. Sarebbe bello fare un benchmark però.
    • Sì, sono vecchio… e molto supponente!
    • Voi ragazzi scendere il mio prato!
    • Penso che sia stato Edwin Dijkstra citato: ottimizzazione prematura è la radice di ogni male… quindi a volte penso che, al diavolo le prestazioni in argomento. Utilizzare il set dove l’imposta deve essere applicata anche nella logica e nel rendere il vostro algoritmo comprensibile. Ma a volte la logica di business può essere molto più chiara e più adattabili quando si va riga per riga. Beh, questo è il mio parere ovviamente.
    • questo è un Knuth citazione, ed è Edgar Dijkstra, ma sono d’accordo con il resto della tua frase 🙂
    • Divertente trovo la logica di business è quasi sempre più chiaro nel set base di codice.

  3. 19

    C’è una risposta di cui sopra che dice “i cursori sono il modo più LENTO per accedere ai dati all’interno di SQL Server… i cursori sono più di trenta volte più lento di base alternative”.

    Questa dichiarazione può essere vero in molti casi, ma come una dichiarazione coperta è problematico. Per esempio, ho fatto buon uso dei cursori in situazioni in cui voglio eseguire un’operazione di aggiornamento o eliminazione influenzano il numero di righe di una tabella di grandi dimensioni che è oggetto di costante produzione legge. Esecuzione di una stored procedure che fa questi aggiornamenti una riga alla volta, finisce per essere più veloce di operazioni basate su set, perché la base di operazione è in conflitto con l’operazione di lettura e si conclude con terribili problemi di blocco (e può uccidere il sistema di produzione interamente in casi estremi).

    In assenza di altre attività di database, basati su un insieme di operazioni sono universalmente più veloce. Nei sistemi di produzione, dipende.

    • Suona come l’eccezione che conferma la regola.
    • Coehoorn]: non ho mai capito che dicendo.
    • A. Lowe] phrases.org.uk/meanings/exception-that-proves-the-rule.html capire eccezione come “ciò che è lasciato fuori” e si noti che la regola qui è qualcosa di simile a “nella maggior parte delle situazione cursori sono cattivi”.
    • grazie per il link, ora ho capito la frase anche meno!
    • A. Lowe] Fondamentalmente dicendo che se si “infrangere una regola” con un subcase, ci deve essere una regola generale per rompere, ergo, esiste una regola. ad esempio Dal Link: (“Se abbiamo una dichiarazione come ‘ingresso è gratuito la domenica’, si può ragionevolmente supporre che, come regola generale, l’ingresso è a pagamento.”)
    • ok che abbia un senso, così come non si applica qui?
    • Penso hes dicendo che quando ci sono problemi di blocco / memoria vincoli di utilizzare i cursori. Questo implica che non si devono usare i cursori altrimenti.
    • Un altro esempio di appropriato utilizzo di cursore: support.microsoft.com/kb/973849 . La versione originale usato il set base di tecniche e ha avuto problemi di blocco.
    • Ed è interessante notare che, il tipo di set di dati stanno parlando (lo stato di sessione dati da applicazioni web) esattamente soddisfa il criterio che ho citato nel post originale– un insieme di dati in cui tante righe quanti sono stati colpiti da un’operazione, ma la tabella stessa riceve costante produzione legge. La versione originale di questo sarebbe stata la morte per un sito con un notevole volume di traffico.

  4. 9

    Cursori tendono ad essere utilizzato da inizio SQL sviluppatori in luoghi dove basati su un insieme di operazioni sarebbe meglio. In particolare quando le persone imparano SQL dopo l’apprendimento di un linguaggio di programmazione tradizionale, il “scorrere questi record” mentalità tende a portare la gente a usare i cursori in modo inappropriato.

    Più gravi SQL libri di includere un capitolo, intimando l’uso di cursori, ben scritto, è chiaro che i cursori hanno il loro posto, ma non dovrebbe essere utilizzato per le operazioni basate su.

    Ci sono ovviamente situazioni in cui i cursori sono la scelta giusta, o almeno di Una scelta corretta.

  5. 9

    L’ottimizzatore spesso non è possibile utilizzare l’algebra relazionale per trasformare il problema quando un cursore viene utilizzato il metodo. Spesso un cursore è un ottimo modo per risolvere un problema, ma l’SQL è un linguaggio dichiarativo, e c’è un sacco di informazioni nel database, i vincoli, le statistiche e indici che significa che l’ottimizzatore ha un sacco di opzioni per risolvere il problema, mentre un cursore praticamente esplicitamente dirige la soluzione.

  6. 8

    In Oracle PL/SQL i cursori non causare blocchi di tabella e, è possibile l’utilizzo di massa-la raccolta di massa di recupero.

    In Oracle 10, spesso usato cursore implicito

      for x in (select ....) loop
        --do something 
      end loop;

    recupera implicitamente 100 righe alla volta. Esplicito di massa-la raccolta di massa di recupero è anche possibile.

    Tuttavia PL/SQL i cursori sono qualcosa di ultima istanza, di utilizzare quando si è in grado di risolvere un problema con il set di base di SQL.

    Un altro motivo è la parallelizzazione, è più facile per il database di parallelizzare grande basati su un insieme di istruzioni che riga per riga codice imperativo. È lo stesso motivo per cui la programmazione funzionale diventa sempre più popolare (Haskell, F#, Lisp, C#, LINQ, MapReduce …), la programmazione funzionale fa di parallelizzazione più facile. Il numero di Cpu per computer è in aumento così parallelizzazione diventa sempre più un problema.

  7. 6

    In generale, perché su un database relazionale, le prestazioni di codice utilizzando i cursori è un ordine di grandezza peggio di operazioni basate su set.

    • hai un punto di riferimento o di riferimento per questo? non ho notato alcuna tale drastico peggioramento delle prestazioni… ma forse la mia tabelle di non avere abbastanza righe per materia (milioni di euro o meno, di solito)?
    • oh aspetta vedo quello che voglio dire – ma non mi sarei mai avvocato utilizzo di cursori invece di impostare operazioni, solo non andare agli estremi per evitare i cursori
    • Mi ricordo la prima volta che l’ho fatto SQL, Abbiamo dovuto importare un 50k quotidiana file di dati da un mainframe in un database di SQL Server… ho usato un cursore, e ha scoperto che l’importazione è stata l’assunzione di circa 26 ore, utilizzando il cursore… Quando ho cambiato basa su un insieme di operazioni, il processo è durato 20 minuti.
  8. 6

    Le risposte di cui sopra non hanno mai sottolineare abbastanza l’importanza di chiusura. Io non sono un grande fan di cursori, perché spesso il risultato in tabella i blocchi a livello di.

    • sì, grazie! Senza opzioni per evitare che (solo lettura, solo in avanti, ecc) certamente, come ogni (sql server) operazione che procede ad occupare più righe e poi le varie pagine di righe.
    • ?? Quello è un problema con la vostra strategia di blocco NON cursori. Anche un’istruzione SELECT aggiungere blocchi di lettura.
  9. 3

    Per quel che vale ho letto che l ‘ “uno” posizionare il cursore verrà a svolgere il suo set a base di controparte è in un totale di esecuzione. Su un piccolo tavolo la velocità di riassumere le righe su ordine di colonne favorisce il set di base di funzionamento, ma come l’aumento della tabella nella dimensione di una riga il cursore diventerà più veloce perché si può semplicemente portare l’esecuzione di valore totale per il passo successivo del ciclo. Ora dove si dovrebbe fare un totale di esecuzione è un argomento diverso…

    • Se intendi per “totale parziale” un’aggregazione di qualche tipo (min, max, sum), competente DBMS battere i pantaloni fuori di un lato client, cursore a base di soluzione, se non altro perché la funzione è eseguita a motore e non c’è nessun client <–> sovraccarico del server. Forse SQL Server non è competente?
    • T]: stiamo discutendo i cursori sul lato server, come all’interno di una stored procedure, non i cursori sul lato client; scusate per la confusione!!!
  10. 2

    Al di fuori della performance (non)problemi, penso che il più grande difetto di cursori si sono dolorosi per eseguire il debug. Soprattutto rispetto al codice nella maggior parte delle applicazioni client dove il debug tende ad essere relativamente facile e funzionalità del linguaggio tendono ad essere molto più facile. Infatti, io sostengo che quasi nulla di quello che si sta facendo in SQL con un cursore che, probabilmente, dovrebbe accadere nel client app in primo luogo.

    • SQL è doloroso per il debug, anche senza i cursori. MS SQL passaggio-attraverso gli strumenti di Visual Studio non sembrano come me (che appendere un sacco, o non viaggio i punti di interruzione in tutto), quindi sono di solito ridotti per le istruzioni di STAMPA 😉
  11. 1

    Puoi pubblicare il cursore esempio o un link per la domanda? Probabilmente c’è un modo ancora migliore di una CTE ricorsiva.

    In aggiunta ad altri commenti, cursori, quando utilizzato in modo improprio (che è spesso causa di inutili pagina/riga serrature.

    • c’è un modo migliore – un freakin cursore 😉
  12. 1

    Si potrebbe probabilmente ha concluso la tua domanda dopo il secondo comma, invece di chiamare la gente “folle”, semplicemente perché hanno un punto di vista diverso di fare e cercando diversamente, per deridere i professionisti che hanno una buona ragione per sentire il modo di che fare.

    In quanto alla tua domanda, mentre ci sono certamente situazioni in cui un cursore può essere chiamato per, nella mia esperienza, gli sviluppatori di decidere che il cursore “deve” essere utilizzato MOLTO più spesso di quanto è effettivamente il caso. La possibilità che qualcuno erranti sul lato del troppo uso di cursori non sono in uso, quando si deve è MOLTO superiore a mio parere.

    • si prega di leggere con più attenzione, Tom – la frase esatta era “folle odio”; “odiato” è stato l’oggetto dell’aggettivo “folle”, non “la gente”. L’inglese può essere un po ‘ difficile, a volte 😉
  13. 0

    sostanzialmente 2 blocchi di codice che fanno la stessa cosa. forse è un po ‘ strano esempio, ma si rivela il punto. SQL Server 2005:

    SELECT * INTO #temp FROM master..spt_values
    DECLARE @startTime DATETIME
    
    BEGIN TRAN 
    
    SELECT @startTime = GETDATE()
    UPDATE #temp
    SET number = 0
    select DATEDIFF(ms, @startTime, GETDATE())
    
    ROLLBACK 
    
    BEGIN TRAN 
    DECLARE @name VARCHAR
    
    DECLARE tempCursor CURSOR
        FOR SELECT name FROM #temp
    
    OPEN tempCursor
    
    FETCH NEXT FROM tempCursor 
    INTO @name
    
    SELECT @startTime = GETDATE()
    WHILE @@FETCH_STATUS = 0
    BEGIN
    
        UPDATE #temp SET number = 0 WHERE NAME = @name
        FETCH NEXT FROM tempCursor 
        INTO @name
    
    END 
    select DATEDIFF(ms, @startTime, GETDATE())
    CLOSE tempCursor
    DEALLOCATE tempCursor
    
    ROLLBACK 
    DROP TABLE #temp

    l’unico aggiornamento 156 ms, mentre il cursore prende 2016 ms.

    • ebbene sì, si rivela il punto che questo è davvero stupido modo per utilizzare un cursore! ma cosa succede se l’aggiornamento di ogni riga dipendeva il valore della prima riga in ordine di data?
    • BEGIN TRAN SELECT TOP 1 baseval FROM tabella ORDER BY timestamp DESC INSERISCI tabella (campi) VALUES (vals, compreso il valore derivato dalla precedente record) COMMIT TRAN
    • che vorresti inserire una riga basato sull’ultima riga con la data, non aggiornare ogni riga da un valore dalla prima fila in ordine di data
    • Per utilizzare veramente il cursore si dovrebbe usare in CUI la CORRENTE DI aggiornamento

Lascia un commento