Query con risultati di default

di il
2 risposte

Query con risultati di default

Ciao
Ho una tabella con i seguenti dati di esempio
Id, IdParent, Version
1,1,null
2,2,null
3,3,null
4,100,null
5,100,1
6,100,2
7,100,3

Vorrei scrivere una query, single row se possibile, che mi restituisca, dato un solo parametro tutti i record che abbiano come valore Version = null (default) oppure =@version.
Ad esempio, per @version int = 2 il risultato dovrebbe essere:
1,1,null
2,2,null
3,3,null
6,100,2

Ovvero, restituiscimi tutti i record con IdParent diverso e con Version = 2, se non ci sono record con Version = 2 allora restituiscimi quelli con null.
Null sarebbe un valore di default, ma potrebbe anche essere 0, 1 o altro a seconda delle necessità.

La query non deve restituire il record
4,100,null
in quanto IdParent sarebbe un valore duplicato.
Quindi non basta un WHERE Version = @version or Version is null

Io senza scrivere 2 sottoquery non ne vengo fuori. A dire il vero non le ho neanche scritte ma farei qualcosa tipo:
Trova tutti i record dove Version = 2 as P1
Trova tutti i record diversi da P1.IdParent as P2
Union P1 e P2
Pero' se ci fosse una query piu' sintetica sarebbe meglio, in quanto devo utilizzarla in molte tabelle.

2 Risposte

  • Re: Query con risultati di default

    Salve,
    se ho ben compreso, potremmo utilizzare una windowing function che "ordini con partizionamento per IdPadre", in modo che per ogni "blocco" di IdPadre le righe con Version = @n sortino per prime, seguite dalle altre, filtrando quindi poi in JOIN per la proiezione finale...
    considerando una popolazione
    
    INSERT INTO dbo.t
    	VALUES ( 1, 1, NULL ), ( 2, 2, NULL ), ( 3, 3, NULL )		-- tutte righe "primarie"
    	, ( 4, 100, NULL ), ( 5, 100, 1 ), ( 6, 100, 2 ), ( 7, 100, 3 )	-- righe "innestate"
    	, ( 8, 100, NULL )						-- questo per mostrare che anche il NULL First comparison non crea problematiche
    	;
    
    possiamo trivialmente scrivere
    
    DECLARE @version int = 2;
    DECLARE @sortVersion int = NULL;
    
    -- utilizzo una windowing function che ottiene un ordinamento partizionato per IdParent e poi utilizzo solo [rn] = 1
    WITH cte AS (
    	-- t.[Version] = @version => = 1° riga ordinata, poi gli altri... SQL Server ordina NULL  come "NULL First",
    	-- ma in ogni caso l'uguaglianza precedente porta ad un ordinamento inferiore e quindi corretto
    	SELECT t.Id, ROW_NUMBER() OVER (PARTITION BY t.IdParent ORDER BY 
    		CASE WHEN 				
    			@sortVersion IS NULL THEN
    				-- @sortVersion == NULL, quindi la comparazione e' "secca" sulla @version
    				CASE WHEN t.[Version] = @version THEN 0 ELSE 1 END 
    			ELSE
    				-- @sortVersion e' valorizzara, utilizzo t.[Version] == @sortVersion, per l'ordinamento principale
    				CASE WHEN t.[Version] = @sortVersion THEN 0 ELSE 1 END 
    			END		
    		) AS [rn]
    		FROM dbo.t t
    	)
    	SELECT t.*
    		FROM dbo.t t
    			JOIN cte c ON t.Id = c.Id
    		WHERE c.[rn] = 1
    		ORDER BY t.Id;
    --<---------------------
    Id          IdParent    Version
    ----------- ----------- -----------
    1           1           NULL
    2           2           NULL
    3           3           NULL
    6           100         2
    
    si puo' valorizzare @sortVersion per la comparazione di ordinamento come desiderato.. in caso NON ci siano corrispondenze nel "gruppo" IdPadre, NULL First vince e sorta come primo elemento e, nel caso di molteplici NULL nel "gruppo", l'ordinamento e' ovviamente casuale, non abbiamo qui regola di come ordinarli...

    spero di aver correttamente interpretato la richiesta
    salutoni romagnoli
    --
    Andrea
  • Re: Query con risultati di default

    Vediamo se ho capito:
    Qui esegui n partizioni in funzione dell'IdParent, se nel record il campo [Version] corrisponde alla @version richiesta imponi 0 come valore per l'ordinamento, altrimenti 1. Quindi il record richiesto sarà sempre il primo della sua partizione (ovvero con RowNumber = 1)
    	SELECT t.Id, ROW_NUMBER() 
    		OVER (PARTITION BY t.IdParent 
    			ORDER BY 
    				CASE WHEN 				
    					@sortVersion IS NULL THEN						
    						CASE WHEN t.[Version] = @version THEN 0 ELSE 1 END 
    					ELSE						
    						CASE WHEN t.[Version] = @sortVersion THEN 0 ELSE 1 END 
    					END		
    				) AS [RowNumber]
    	FROM dbo.t t

    Poi qui vai semplicemente a pescare tutti i record con RowNumber=1
    	SELECT t.*
    		FROM dbo.t t
    			JOIN cte c ON t.Id = c.Id
    		WHERE c.[RowNumber] = 1
    		ORDER BY t.Id;
    Che dire, grazie mille!!!
    Mi piace come soluzione
Devi accedere o registrarti per scrivere nel forum
2 risposte