Domanda

Posso capire il voler evitare di dover usare un cursore a causa dell'overhead e dell'inconveniente, ma sembra che ci sia un certo cursore-fobia-mania in atto in cui le persone stanno facendo di tutto per evitare di doverne usare uno.

/ p>

Ad esempio, una domanda chiedeva come fare qualcosa di ovviamente banale con un cursore e la risposta accettata proposta usando una query ricorsiva con espressione di tabella comune (CTE) con una funzione personalizzata ricorsiva, anche se questo limita il numero di righe che potrebbero essere elaborato a 32 (a causa del limite di chiamata di funzione ricorsiva nel server sql). Questo mi sembra una terribile soluzione per la longevità del sistema, per non parlare di uno sforzo tremendo solo per evitare di usare un semplice cursore.

Qual è la ragione di questo livello di odio folle? Qualche "autorità nota" ha emesso una fatwa contro i cursori? Qualche male indicibile si nasconde nel cuore dei cursori che corrompe la morale dei bambini o qualcosa del genere?

Domanda Wiki, più interessata alla risposta che al rappresentante.

Informazioni correlate:

Cursori di avanzamento rapido di SQL Server

EDIT: vorrei essere più preciso: capisco che i cursori non dovrebbero essere usati al posto delle normali operazioni relazionali ; questo è un gioco da ragazzi. Quello che non capisco è che le persone si sforzano di evitare i cursori come se avessero cootie o qualcosa del genere, anche quando un cursore è una soluzione più semplice e / o più efficiente. È l'odio irrazionale che mi confonde, non le evidenti efficienze tecniche.

È stato utile?

Soluzione

Il " overhead " con i cursori fa semplicemente parte dell'API. I cursori sono il modo in cui parti dell'RDBMS funzionano sotto il cofano. Spesso CREATE TABLE e INSERT hanno istruzioni SELECT e l'implementazione è l'implementazione del cursore interno evidente.

Utilizzo di operatori di livello superiore "basati su set" raggruppa i risultati del cursore in un unico set di risultati, il che significa meno API avanti e indietro.

I cursori precedono le lingue moderne che offrono raccolte di prima classe. La vecchia C, COBOL, Fortran, ecc., Dovevano elaborare le righe una alla volta perché non esisteva la nozione di "raccolta". che potrebbe essere ampiamente utilizzato. Java, C #, Python, ecc., Hanno strutture di elenco di prima classe per contenere set di risultati.

The Slow Issue

In alcuni ambienti, i join relazionali sono un mistero e la gente scriverà cursori nidificati anziché un semplice join. Ho visto operazioni di loop nidificati veramente epiche scritte come molti e molti cursori. Sconfiggere un'ottimizzazione RDBMS. E correndo molto lentamente.

Le riscritture SQL semplici per sostituire i loop di cursore nidificati con join e un singolo loop di cursore piatto possono eseguire i programmi al 100 ° tempo. [Pensavano che fossi il dio dell'ottimizzazione. Tutto quello che ho fatto è stato sostituire i loop nidificati con join. Cursori ancora usati.]

Questa confusione porta spesso a un'accusa di cursori. Tuttavia, non è il cursore, è l'abuso del cursore che è il problema.

Il problema delle dimensioni

Per insiemi di risultati davvero epici (ovvero, scaricare una tabella in un file), i cursori sono essenziali. Le operazioni basate su set non possono materializzare set di risultati molto grandi come un'unica raccolta in memoria.

Alternative

Cerco di utilizzare un livello ORM il più possibile. Ma questo ha due scopi. Innanzitutto, i cursori sono gestiti dal componente ORM. In secondo luogo, l'SQL è separato dall'applicazione in un file di configurazione. Non è che i cursori siano cattivi. È che codificare tutte quelle aperture, chiusure e recuperi non è una programmazione a valore aggiunto.

Altri suggerimenti

I cursori fanno sì che le persone applichino eccessivamente una mentalità procedurale a un ambiente basato su set.

E sono LENTO !!!

Da SQLTeam :

  

Nota che i cursori sono i   Il modo più lento per accedere ai dati all'interno di SQL   Server. Dovrebbe essere usato solo quando   devi veramente accedere a una riga in a   tempo. L'unica ragione a cui riesco a pensare   per quello è chiamare una procedura memorizzata   su ogni riga. Nel Cursore   Articolo di performance ho scoperto   che i cursori sono oltre trenta volte   più lento delle alternative basate sul set .

C'è una risposta sopra che dice che i cursori sono il modo PIÙ LENTO per accedere ai dati all'interno di SQL Server ... i cursori sono oltre trenta volte più lenti delle alternative basate su set. "

Questa affermazione può essere vera in molte circostanze, ma come affermazione generale è problematica. Ad esempio, ho fatto buon uso dei cursori in situazioni in cui desidero eseguire un aggiornamento o eliminare un'operazione che interessa molte righe di una tabella di grandi dimensioni che riceve letture di produzione costanti. L'esecuzione di una procedura memorizzata che esegue questi aggiornamenti una riga alla volta risulta essere più veloce delle operazioni basate su set, poiché l'operazione basata su set è in conflitto con l'operazione di lettura e finisce per causare orribili problemi di blocco (e potrebbe uccidere completamente il sistema di produzione, in casi estremi).

In assenza di altre attività del database, le operazioni basate su set sono universalmente più veloci. Nei sistemi di produzione, dipende.

I cursori tendono ad essere utilizzati iniziando gli sviluppatori SQL in luoghi in cui le operazioni basate su set sarebbero migliori. Soprattutto quando le persone imparano SQL dopo aver appreso un linguaggio di programmazione tradizionale, l '"iterazione su questi record" la mentalità tende a indurre le persone a usare i cursori in modo inappropriato.

I libri SQL più seri includono un capitolo che impone l'uso dei cursori; quelli ben scritti chiariscono che i cursori hanno il loro posto ma non dovrebbero essere usati per operazioni basate su set.

Ci sono ovviamente situazioni in cui i cursori sono la scelta corretta, o almeno una scelta corretta.

L'ottimizzatore spesso non può utilizzare l'algebra relazionale per trasformare il problema quando si utilizza un metodo cursore. Spesso un cursore è un ottimo modo per risolvere un problema, ma SQL è un linguaggio dichiarativo e ci sono molte informazioni nel database, dai vincoli, alle statistiche e agli indici, il che significa che l'ottimizzatore ha molte opzioni per risolvere il problema, mentre un cursore dirige in modo esplicito la soluzione.

In Oracle i cursori PL / SQL non comporteranno blocchi della tabella ed è possibile utilizzare la raccolta di massa / recupero di massa.

In Oracle 10 il cursore implicito spesso usato

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

recupera implicitamente 100 righe alla volta. È anche possibile la raccolta / recupero in blocco espliciti.

Tuttavia i cursori PL / SQL sono l'ultima risorsa, usali quando non sei in grado di risolvere un problema con SQL basato su set.

Un altro motivo è la parallelizzazione, è più facile per il database parallelizzare istruzioni basate su set di grandi dimensioni rispetto al codice imperativo riga per riga. È lo stesso motivo per cui la programmazione funzionale diventa sempre più popolare (Haskell, F #, Lisp, C # LINQ, MapReduce ...), la programmazione funzionale semplifica la parallelizzazione. Il numero di CPU per computer sta aumentando, quindi la parallelizzazione diventa sempre più un problema.

In generale, perché su un database relazionale, l'esecuzione del codice usando i cursori è un ordine di grandezza peggiore delle operazioni basate su set.

Le risposte sopra non hanno sottolineato abbastanza l'importanza del blocco. Non sono un grande fan dei cursori perché spesso provocano blocchi a livello di tabella.

Per quello che vale, ho letto che il "uno" posiziona un cursore per eseguire la sua controparte basata su set è in un totale parziale. Su una piccola tabella la velocità di sommare le righe sull'ordine per colonne favorisce l'operazione basata su set ma quando la tabella aumenta di dimensioni della riga il cursore diventerà più veloce perché può semplicemente trasportare il valore totale corrente al passaggio successivo del ciclo continuo. Ora dove dovresti fare un totale parziale è un argomento diverso ...

Al di fuori dei problemi di performance (non), penso che il più grande fallimento dei cursori sia che sono dolorosi per il debug. Soprattutto rispetto al codice nella maggior parte delle applicazioni client in cui il debug tende ad essere relativamente semplice e le funzionalità del linguaggio tendono ad essere molto più facili. In realtà, sostengo che quasi tutto ciò che si sta facendo in SQL con un cursore dovrebbe probabilmente accadere in primo luogo nell'app client.

Puoi pubblicare quell'esempio di cursore o un link alla domanda? C'è probabilmente un modo persino migliore di un CTE ricorsivo.

Oltre ad altri commenti, i cursori se usati in modo improprio (che spesso è) causano blocchi inutili di pagina / riga.

Probabilmente avresti potuto concludere la tua domanda dopo il secondo paragrafo, piuttosto che chiamare le persone "pazze" semplicemente perché hanno un punto di vista diverso rispetto a te e cercano comunque di deridere i professionisti che potrebbero avere un'ottima ragione per sentirsi come loro.

Per quanto riguarda la tua domanda, mentre ci sono certamente situazioni in cui un cursore può essere richiesto, nella mia esperienza gli sviluppatori decidono che un cursore "deve" essere usato molto più spesso di quanto non sia effettivamente il caso. A mio avviso, la possibilità che qualcuno commetta errori sul lato dell'uso eccessivo dei cursori rispetto al non usarli quando dovrebbero dovrebbe essere MOLTO più elevata.

in pratica 2 blocchi di codice che fanno la stessa cosa. forse è un esempio un po 'strano ma dimostra 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

il singolo aggiornamento richiede 156 ms mentre il cursore richiede 2016 ms.

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