Sql con inner join

di il
3 risposte

Sql con inner join

Salve a tutti

ho bisogno di aiuto su una select strana che non riesco a fare.
Cerco di spiegare ul problema:

ho due tabelle T1 e T2 dove su T1 ho due codici prodotto, uno e' il codice originale
e ll'atro e l'alias. In T2 posso trovare sia prodotti con codice prodotto originale che con alias.

La parte che mi interessa e' estrarre da T2 tutti i prodotti sia originale che alias.
Questo e' l'approccio che ho fatto:
begin try drop table #Find end try begin catch end catch
select ROW_NUMBER() over ( order by A.Cod1 ) as RowNumber,
a.Cod1 ,
a.descr ,
b.data
INTO #Find
FROM T1 as A
INNER JOIN T2 as B on A.cod1=B.cod or a.alias = b.cod
group by a.cod1, a.descr, b.data
order by A.cod
select * from #Find
ora la inner join fatta in quella maniera con la or e' bruttissima e pesantissima
la soluzione potrebbe essere aggiungere una seconda inner join in questo modo

INNER JOIN T2 as B on A.cod1=B.cod
INNER JOIN T2 as C on a.alias = C.cod

ma non so perche' non funziona.

Chiedo un suggerimento a voi.

3 Risposte

  • Re: Sql con inner join

    Salve,
    ridotto all'osso, il comando eseguito diventa
    
    SELECT ROW_NUMBER() OVER (ORDER BY a.[cod]) AS [rn]
    	, a.[cod]
    	, b.[data]
    	FROM @t1 a
    		JOIN @t2 b ON a.[cod] = b.[cod] OR a.[alias] = b.[cod]
    	GROUP BY a.[cod], b.[data]
    	ORDER BY a.cod;
    
    che ti ritorna il risultato desiderato, mentre tu preferiresti
    
    SELECT ROW_NUMBER() OVER (ORDER BY a.[cod]) AS [rn]
    	, a.[cod]
    	, b.[data]
    	FROM @t1 a
    		JOIN @t2 b ON a.[cod] = b.[cod] 
    		JOIN @t2 c ON a.[alias] = c.[cod]
    	GROUP BY a.[cod], b.[data]
    	ORDER BY a.cod;
    
    ma non ottieni il risultato sperato per il semplice principio di funzionamento della JOIN, che esclude dal risultato quanto non contrapponibile....

    potresti eventualmente "risolvere" diversamente con una UNION, che utilizzata con una common table expression ti diventerebbe simile a
    
    WITH cte AS (
    	SELECT a.[cod]
    		, b.[data]
    		FROM @t1 a
    			JOIN @t2 b ON a.[cod] = b.[cod]
    	UNION
    	SELECT a.[cod]
    		, b.[data]
    		FROM @t1 a
    			JOIN @t2 b ON a.[alias] = b.[cod]
    	)
    	SELECT ROW_NUMBER() OVER (ORDER BY c.[cod]) AS [rn] 
    		, c.[cod]
    		, c.[data]
    		FROM cte c
    		GROUP BY c.[cod], c.[data]
    		ORDER BY c.cod;
    

    ma i piani di esecuzione, al di la' ovviamente che lo scenario corrente che e' banlanlmente triviale (senza indici, pochi dati, ...), NON sono assolutamente rinfrancanti nella seconda ipotesi...
    nel primo caso, quello originale, dove e' previsto
    
    	FROM @t1 a
    		JOIN @t2 b ON a.[cod] = b.[cod] OR a.[alias] = b.[cod]
    
    , nella mia rappresentazione banale prevede 2 scan completi con un nested loop derivante dalla join... l'operatore OR in questo caso consente con semplicita' la risoluzione della lookup senza sforzi addizionali...
    
      |--Sequence Project(DEFINE:([Expr1004]=row_number))
           |--Segment
                |--Sort(DISTINCT ORDER BY:([a].[cod] ASC, [b].[data] ASC))
                     |--Nested Loops(Inner Join, WHERE:(@t1.[cod] as [a].[cod]=@t2.[cod] as [b].[cod] OR @t1.[alias] as [a].[alias]=@t2.[cod] as [b].[cod]))
                          |--Table Scan(OBJECT:(@t2 AS [b]))
                          |--Table Scan(OBJECT:(@t1 AS [a]))
    
    l'utilizzo della UNION, dove ci sono 2 set filtrati, uno per il confronto su [cod] e l'altro per il confronto su [alias]
    
    	FROM @t1 a
    		JOIN @t2 b ON a.[cod] = b.[cod]
    UNION ...
    	FROM @t1 a
    		JOIN @t2 b ON a.[alias] = b.[cod];
    
    prevede ulteriori passaggi intermedi, sempre in questo scenario banale, oltre alla lookup eseguita con un nested loop (JOIN di cui sopra ma qui comunque funzionale ed utilizzata), richiedendo anche l'hash match della UNION
    
      |--Sequence Project(DEFINE:([Expr1010]=row_number))
           |--Segment
                |--Sort(DISTINCT ORDER BY:([Union1008] ASC, [Union1009] ASC))
                     |--Concatenation
                          |--Nested Loops(Inner Join, WHERE:(@t2.[cod] as [b].[cod]=@t1.[cod] as [a].[cod]))
                          |    |--Sort(ORDER BY:([b].[cod] ASC, [b].[data] ASC))
                          |    |    |--Table Scan(OBJECT:(@t2 AS [b]))
                          |    |--Table Scan(OBJECT:(@t1 AS [a]))
                          |--Hash Match(Inner Join, HASH:([a].[alias])=([b].[cod]), RESIDUAL:(@t2.[cod] as [b].[cod]=@t1.[alias] as [a].[alias]))
                               |--Table Scan(OBJECT:(@t1 AS [a]))
                               |--Table Scan(OBJECT:(@t2 AS [b]))
    
    cosa non ti piace nel tuo codice originario, al di la' della "brutta abitudine" di utilizzo di un "alias" sulla chiave di riferimento alla tabella esterna?

    salutoni romagnoli
    --
    Andrea
  • Re: Sql con inner join

    Grazie della spiegazione Andrea, ho seguito il tuo consiglio ed e' perfetto. Ho risolto con una union e i tempi sono
    migliori di quanto mi aspettassi.
    Una domanda visto che sei efferato su sql server ed io non proprio.
    Nell'sql vecchio viene fatto uso di un campo # temporaneo. In fondo alla Union c'era
    una select su questo file temporaneo. Mi va in errore .

    Questa e' la soluzione che ho adottato
    begin try  drop table #Cerca  end try  begin catch  end catch  
    select ROW_NUMBER() over  ( 	order by Codice  ) as RowNumber,
    from (
    			Select
    			a.cod1  as Codice , 
    			a.Descrizione,
                            B.Data
                            FROM Prodotti as A 
    			INNER JOIN Ordini as B on A.Cod1=b.Cod 
                            Union
    			Select
    			C.cod1  as Codice , 
    			C.Descrizione,
                            D.Data
                            FROM Prodotti as C 
    			INNER JOIN Ordini as D on A.Alias=b.Cod 
     		) #Cerca
    		select * from #Cerca
    Se eseguo cosi, Sql Server mi da' un errore
    Msg 208, Level 16, State 0, Line 44
    Invalid object name '#Cerca'.

    Se tolgo la select e' ok. Qualche buon anima mi potrebbe spiegare il perche'?
    Grazie
  • Re: Sql con inner join

    Salve Massimiliano,
    Una domanda visto che sei efferato su sql server ed io non proprio.
    efferato non e' una bella cosa da dire, http://www.treccani.it/vocabolario/efferato


    nell'ultima query, hai mal espresso la formula SELECT ... INTO...
    di seguito,
    
    PRINT 'mio consiglio spassionato e MOLTO PERSONALE, cambia "modo" di scrivere :D';
    PRINT 'un prettyfier del codice lo rende piu' ordinato, leggibile, comprensibile, modificabile :D ';'
    PRINT 'vedi ad esempio https://www.amazon.it/Joe-Celkos-SQL-Programming-Style/dp/0120887975';
    
    BEGIN TRY
        IF OBJECT_ID ( '#Cerca' ) IS NOT NULL 
    		DROP TABLE Cerca;
    END TRY
    BEGIN CATCH
    		-- ritorna l'errore
    		DECLARE @ErrorMSG varchar(2000);
    		SET @ErrorMSG = ERROR_MESSAGE();
    		RAISERROR (@ErrorMSG, 16, 1);
    		-- se e' una stored procedure, oltre a sollevare l'eccezione,
    		-- RITORNA un valore in "ret code" che sia diverso da 0
    		-- ad esempio:
    --		RETURN -100;
    END CATCH;
    
    SELECT ROW_NUMBER() OVER ( ORDER BY [Codice]  ) as [RowNumber]
    	, sub.[Codice], sub.[Descrizione], sub.[Data]
    
    	INTO #Cerca --   <--- non avevi effettuato la generazione della temp table con il costrutto SELECT... INTO
    
    	FROM (
    			SELECT a.[Cod1] AS [Codice]
    				, a.[Descrizione] AS [Descrizione]
    				, b.[Data] AS [Data]
    				FROM [dbo].[Prodotti] a
    					JOIN [dbo].[Ordini] b ON a.[Cod1] = b.[Cod]
    			UNION
    			SELECT a.[Cod1] AS [Codice]
    				, a.[Descrizione] AS [Descrizione]
    				, b.[Data] AS [Data]
    				FROM [dbo].[Prodotti] a
    					JOIN [dbo].[Ordini] b ON a.[Alias] = b.[Cod]
    			) sub;
    
    SELECT * 
    	FROM #Cerca;
    GO
    
    ma se la proiezione "completa" e' quella indicata, a mio parere non serve neache una temp table,
    
    PRINT 'ma per me, se il codice e'' quello indicato, non serve una temp table e puoi tranquillamente andare di proiezione standard';
    
    SELECT ROW_NUMBER() OVER ( ORDER BY [Codice]  ) as [RowNumber]
    	, sub.[Codice], sub.[Descrizione], sub.[Data]
    
    	FROM (
    			SELECT a.[Cod1] AS [Codice]
    				, a.[Descrizione] AS [Descrizione]
    				, b.[Data] AS [Data]
    				FROM [dbo].[Prodotti] a
    					JOIN [dbo].[Ordini] b ON a.[Cod1] = b.[Cod]
    			UNION
    			SELECT a.[Cod1] AS [Codice]
    				, a.[Descrizione] AS [Descrizione]
    				, b.[Data] AS [Data]
    				FROM [dbo].[Prodotti] a
    					JOIN [dbo].[Ordini] b ON a.[Alias] = b.[Cod]
    			) sub;
    
    e, a dire il vero, IO NON sono un amante delle sub-queries direttamente innestate, preferisco definirle in una common table expression diversa e poi usarle nelle queries...
    mi da anche un senso di chiarezza e leggibilita'' migliore..
    
    WITH cte AS (
    	SELECT a.[Cod1] AS [Codice]
    		, a.[Descrizione] AS [Descrizione]
    		, b.[Data] AS [Data]
    		FROM [dbo].[Prodotti] a
    			JOIN [dbo].[Ordini] b ON a.[Cod1] = b.[Cod]
    	UNION
    	SELECT a.[Cod1] AS [Codice]
    		, a.[Descrizione] AS [Descrizione]
    		, b.[Data] AS [Data]
    		FROM [dbo].[Prodotti] a
    			JOIN [dbo].[Ordini] b ON a.[Alias] = b.[Cod]
    	)
    SELECT ROW_NUMBER() OVER ( ORDER BY [Codice]  ) as [RowNumber]
    	, c.[Codice], c.[Descrizione], c.[Data]
    	FROM cte c
    	ORDER BY c.[Codice];
    
    un saluto romagnolo
    --
    Andrea
Devi accedere o registrarti per scrivere nel forum
3 risposte