22 messaggi dal 03 settembre 2008
Salve a tutti, sto avendo problemi nel costruire un cursore, questa è la dichiarazione:

--------
DECLARE @intParameter int
DECLARE db_cursor CURSOR FOR
SELECT IDAnagrafica AS IDFornitore
FROM t_Gec_Anagrafica_Forniture
WHERE (Fine IS NULL) AND (IDMerce = 1)
GROUP BY IDAnagrafica
--------

Il risultato della query eseguita da sola restituisce, correttamente, tre righe:
IDAnagrafica
1
2
3

Quando però eseguo il seguente ciclo, viene iterato solo una volta...

--------

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @intParameter

WHILE @@FETCH_STATUS = 0
BEGIN
SELECT TOP 1 t_Gec_Anagrafica.CognomeRagSoc, t_Gec_Anagrafica_Forniture.Costo, t_Gec_Anagrafica_Forniture.GGConsegna, t_Gec_Anagrafica_Forniture.Inizio
FROM t_Gec_Anagrafica INNER JOIN
t_Gec_Anagrafica_Forniture ON t_Gec_Anagrafica.ID = t_Gec_Anagrafica_Forniture.IDAnagrafica
WHERE t_Gec_Anagrafica_Forniture.Fine IS NULL AND t_Gec_Anagrafica_Forniture.IDMerce = 1
AND t_Gec_Anagrafica_Forniture.IDAnagrafica = @intParameter
ORDER BY t_Gec_Anagrafica_Forniture.Inizio DESC

FETCH NEXT FROM db_cursor INTO @intParameter
END

CLOSE db_cursor
DEALLOCATE db_cursor

-------

Qualcuno potrebbe gentilmente aiutarmi a capire cosa sbaglio?
Grazie mille

Federico
1.976 messaggi dal 27 luglio 2005
Contributi
salve,
personalmente non vedo perche' tu debba usare una cosa cosi' "brutta" come un cursore..
non l'ho guardato piu' che bene, ma mi pare tu possa addivenire a una soluzione molto piu' elegante e sicuramente piu' efficiente utilizzando le funzionalita' native di SQL Server basate completamente su set di dati e non su porcherie quali i cursori..
di seguito un esempio triviale,
SET NOCOUNT ON;
USE tempdb;
GO
CREATE TABLE dbo.t (
  Id int
  );
CREATE TABLE dbo.t2 (
  Id int,
  IdT int
  );
GO
INSERT INTO dbo.t VALUES (1), (2) , (2), (3), (3), (3);
INSERT INTO dbo.t2 VALUES (1, 1), (2,2), (3,2), (4,3), (5,3), (6,3);
GO
SELECT Id
  FROM dbo.t
  GROUP BY Id;
PRINT 'che andrebbe meglio riscritto in un DISTINCT';
PRINT 'visto che in effetti l''optimizer cosi'' riscrive';
PRINT 'la query stessa.. vedi ad esempio il piano di esecuzione';
SELECT DISTINCT Id
  FROM dbo.t
  ORDER BY Id;
GO
DECLARE @param int;

DECLARE cur CURSOR FOR
  SELECT DISTINCT Id
    FROM dbo.t
    ORDER BY Id;

OPEN cur;
FETCH NEXT FROM cur INTO @param;
WHILE @@FETCH_STATUS = 0 BEGIN
  PRINT @param;
  
  SELECT TOP (1) t2.Id
    FROM dbo.t2
    WHERE t2.IdT = @param
    ORDER BY t2.Id DESC;
    
  FETCH NEXT FROM cur INTO @param;
END;
CLOSE cur;
DEALLOCATE cur;
GO
CREATE FUNCTION dbo.ufnGetT2 (
  @Id int
  ) RETURNS TABLE
AS 
  RETURN
    SELECT TOP (1) t2.Id
      FROM dbo.t2 t2
      WHERE t2.Idt = @Id
      ORDER BY t2.Id DESC;
GO
WITH cte AS (  
  SELECT DISTINCT Id
    FROM dbo.t
  )
SELECT f.Id
  FROM cte
  CROSS APPLY dbo.ufnGetT2 ( cte.Id ) AS f;
GO
PRINT 'che senza ufn si puo'' anche riscrivere in';
WITH cte AS (  
  SELECT DISTINCT Id
    FROM dbo.t
  )
SELECT t2.Id
  FROM cte
  CROSS APPLY (
    SELECT TOP (1) t2.Id
      FROM dbo.t2 t2
      WHERE t2.Idt = cte.Id
      ORDER BY t2.Id DESC
    ) t2;
  
GO
DROP FUNCTION dbo.ufnGetT2;
DROP TABLE dbo.t, dbo.t2;
--<------------


Id
-----------
1
2
3


che andrebbe meglio riscritto in un DISTINCT
visto che in effetti l'optimizer cosi' riscrive
la query stessa.. vedi ad esempio il piano di esecuzione
Id
-----------
1
2
3




1
Id
-----------
1



2
Id
-----------
3



3
Id
-----------
6



Id
-----------
1
3
6


che senza ufn si puo' anche riscrivere in
Id
-----------
1
3
6


ho prima semplificato la tua aggregazione, che di aggregazione ha solo l'indicazione mentre semanticamente tecnicamente si risolve in un DISTINCT, come puoi benissimo riscontrare anche dal piano di esecuzione comunque generato in vece del tuo GROUP BY, anche se si addiviene allo stesso risultato..
successivamente ho dimostrato la funzionalita' del cursore stesso, che comunque viene surclassato sia in eleganza che in performance e utilizzo di risorse dalle soluzioni successive..
una utilizza una funzione utente che restituisce una tabella composta da una singola riga relativa alla riga referenziante la prima tabella con l'id piu' alto (giusto per complicare le cose), dove la seconda utilizza una subquery con il medesimo risultato, e successivamente a tale risultato viene imposto l'operatore CROSS APPLY per l'esecuzione "riga per riga" relativamente al primo risultato derivante dal DISTINCT della tabella referenziata..
come vedi, se ho capito la tua esigenza, non ti serve affatto la bruttura del cursore per ottenere, per ciascun gruppo di fornitura, i riferimenti all'anagrafica fornitore..
saluti

Andrea Montanari
http://www.hotelsole.com - http://www.hotelsole.com/asql/index.php

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.