none
Split e ciclo in una sp RRS feed

  • Domanda

  • Salve a tutti,

    sulla tabella "Clienti" ho un campo che si chiama codice che contiene dei codici separati da SPAZIO.

    Leggo i dati di questa tabella con una sp a cui tra le altre cose passo il parametro CodiceDaCercare che può contenere uno o più codici separati da SPAZIO. 

    Quello che dovrei ottenere è spacchettare i singoli codici contenuti nel parametro e metterli nella clausola Where della query con tutti i valori passati in OR.

    Quindi diciamo per esempio che nel parametro passo una cosa del tipo "AAA BBB CCC", la query SELECT che la SP deve creare sarà del tipo:

    SELECT * from Clienti where Codice like '%AAA%' or Codice like '%BBB%' or Codice like '%CCC%'

    Qualcuno ha suggerimenti? Penso mi serverebbe qualcosa tipo la funzione SPLIT e un ciclo


    Diego Riccardi

    lunedì 7 ottobre 2013 13:40

Risposte

  • Ciao Diego,

    Credo che tu non possa che ricorrere all'SQL Dinamico, quindi andresti a generare la tua query SQL on-the-fly per poi mandarla in esecuzione con una EXEC ().

    Potendo fare a meno del LIKE (che comunque ti porterebbe ad avere una query poco performante se la tabella da interrogare contiene molti record), questa è un'alternativa:

    1. Usa una funzione che converte la tua stringa in input in una tabella (ad esempio: http://ole.michelsen.dk/blog/split-string-to-table-using-transact-sql/)

    2. Modifica la tua query in questo modo:

    SELECT * from Clienti c
    inner join dbo.Split(@Criteria, ' ') crit on c.Codice = crit.Data

    Dove @Criteria è la tua stringa di input con i codici separati da spazi, e dbo.Split() è la funzione di cui sopra.

    Con un indice sul campo "Codice" della tabella Clienti, la query dovrebbe essere abbastanza efficiente.

    HTH,


    Alberto Dallagiacoma
    My Italian Blog: http://blogs.ugidotnet.org/alby
    Twitter: http://twitter.com/albertodall
    DotDotNet - User Group .NET Emilia Romagna: http://www.dotdotnet.org


    lunedì 7 ottobre 2013 14:59
  • Usando SQL dinamico, la SQL Injection è sempre in agguato. :) Avendo la libertà di specificare i parametri di filtro in input, bisogna sempre avere un occhio di riguardo. Consiglio questo sempre attuale articolo: http://www.sommarskog.se/dynamic_sql.html

    Per quanto riguarda il tuo esempio, non capisco come mai ripeti l'"order by", la where condition e metti un "where 1 = 1".

    HTH


    Alberto Dallagiacoma
    My Italian Blog: http://blogs.ugidotnet.org/alby
    Twitter: http://twitter.com/albertodall
    DotDotNet - User Group .NET Emilia Romagna: http://www.dotdotnet.org


    mercoledì 9 ottobre 2013 15:01

Tutte le risposte

  • Ciao Diego,

    Credo che tu non possa che ricorrere all'SQL Dinamico, quindi andresti a generare la tua query SQL on-the-fly per poi mandarla in esecuzione con una EXEC ().

    Potendo fare a meno del LIKE (che comunque ti porterebbe ad avere una query poco performante se la tabella da interrogare contiene molti record), questa è un'alternativa:

    1. Usa una funzione che converte la tua stringa in input in una tabella (ad esempio: http://ole.michelsen.dk/blog/split-string-to-table-using-transact-sql/)

    2. Modifica la tua query in questo modo:

    SELECT * from Clienti c
    inner join dbo.Split(@Criteria, ' ') crit on c.Codice = crit.Data

    Dove @Criteria è la tua stringa di input con i codici separati da spazi, e dbo.Split() è la funzione di cui sopra.

    Con un indice sul campo "Codice" della tabella Clienti, la query dovrebbe essere abbastanza efficiente.

    HTH,


    Alberto Dallagiacoma
    My Italian Blog: http://blogs.ugidotnet.org/alby
    Twitter: http://twitter.com/albertodall
    DotDotNet - User Group .NET Emilia Romagna: http://www.dotdotnet.org


    lunedì 7 ottobre 2013 14:59
  • Grazie per la risposta, ho provato e funziona!

    Solo una cosa se un record della tabella Clienti contiene 2 o più dei criteri impostati, mi ritrovo il record duplicato o triplicato perchè logicamente la JOIN è soddisfatta per più valori e quindi i risultati vengono duplicati.

    Quindi avevo pensato di agire direttamente nella mia sp, io imposto il filtro così:

    IF @Codice <> ''
    Begin
       SELECT @filtro = @filtro + ' AND c.Codice LIKE ''%'' + @xCodice + ''%'''
       select @campi = @campi + ', c.Codice'
    End	

    che ho modificato in:

    IF @Codice <> ''
    Begin
       SELECT @Codice = '''' + replace(@Codice,' ','%'' OR Codice Like ''%') + ''''
       SELECT @filtro = @filtro + ' AND (c.Codice LIKE ''%'' + @xCodice + ''%'')'
       select @campi = @campi + ', c.Codice'
    End	

    Ma la query non restituisce più nessun record.


    Diego Riccardi

    lunedì 7 ottobre 2013 16:01
  • Dal codice che hai postato, non riesco a capire dove tu abbia la join...

    Alberto Dallagiacoma
    My Italian Blog: http://blogs.ugidotnet.org/alby
    Twitter: http://twitter.com/albertodall
    DotDotNet - User Group .NET Emilia Romagna: http://www.dotdotnet.org

    martedì 8 ottobre 2013 12:29
  • Intendevo dire che usando il sistema che mi consigliavi tu con la JOIN mi si duplicano (o triplicano etc...) i risultati quando più condizioni della JOIN sono soddisfatti sullo stesso record della tabella clienti.

    Quindi avevo optato per modificare la mia SP che non utilizza JOIN, ma crea la clausola Where al momento, con il sistema che ho scritto sopra.

    In entrambi i casi non riesco a ottenere il risultato che mi interessa, perchè se nel primo c'è una duplicazione dei risultati, nel secondo caso non mi ritorna nessun record.

    Grazie ancora


    Diego Riccardi

    martedì 8 ottobre 2013 13:03
  • Ah, ok.

    Infatti la strada della funzione Split() è valida se puoi fare a meno del "LIKE", quindi avere uno ed uno solo record della tabella Clienti per ogni criterio di ricerca passato in ingresso.

    Per il secondo caso (nessun record ritornato), ti suggerisco di farti mandare in output il codice SQL generato (es.: PRINT @sqlStmt), e poi eseguire il comando generato in una finestra di query separata, così da poter vedere meglio cosa non quadra.

    HTH,


    Alberto Dallagiacoma
    My Italian Blog: http://blogs.ugidotnet.org/alby
    Twitter: http://twitter.com/albertodall
    DotDotNet - User Group .NET Emilia Romagna: http://www.dotdotnet.org

    martedì 8 ottobre 2013 15:43
  • ci avevo già provato e a me non sembrano esserci errori.

    Questo è l'output 

    'OG1%' OR Ult_cat_Class Like '%OG2%' OR Ult_cat_Class Like '%OG3'
    
    SELECT c.IdCliente, c.RagSoc, c.Citta, c.Codice FROM 
     			 (
    				SELECT  ROW_NUMBER() OVER(ORDER BY IdCliente) AS rowNumber, c.IdCliente, c.RagSoc, c.Citta, c.Codice
    				FROM    dbo.qry_SP_CercaClienti c where 1 = 1  AND (c.Codice LIKE '%' + @xCodice + '%')
    			 ) AS c
    			 WHERE  (rowNumber >= 1 AND
    			 rowNumber <  101) AND (c.Codice LIKE '%' + @xCodice + '%') ORDER BY IdCliente

    Dove il primo rigo è il contenuto della variabile @Codice (print @Codice) e dopo c'è la query completa che eseguo con:

    SELECT @paramlist = '@xCodice nvarchar(100) = NULL'
    
    EXEC sp_executesql @sql, @paramlist, @Codice

    Eseguendo la query mostrata in output in una nuova finestra e sostituendo a @xCodice il contenuto del primo rigo, la query viene eseguita correttamente restituendo i record esatti.

    Grazie ancora per l'aiuto


    Diego Riccardi

    mercoledì 9 ottobre 2013 08:27
  • Dopo numerose prove forse ho trovato una soluzione anche se non so se convincente al 100%. Ho modificato così:

    SELECT @Codice = replace(@Codice,' ','%'' OR Codice Like ''%') 
    SELECT @filtro = @filtro + ' AND (c.Codice LIKE ''%' + @Codice + '%'')'

    Quindi "sparo" direttamente la variabile @Codice nella where senza passare per la @xCodice che poi passo alla EXEC come mostrato sopra.

    In questo modo funziona, ma non è che potrei avere problemi di SQL Injection?


    Diego Riccardi

    mercoledì 9 ottobre 2013 08:48
  • Usando SQL dinamico, la SQL Injection è sempre in agguato. :) Avendo la libertà di specificare i parametri di filtro in input, bisogna sempre avere un occhio di riguardo. Consiglio questo sempre attuale articolo: http://www.sommarskog.se/dynamic_sql.html

    Per quanto riguarda il tuo esempio, non capisco come mai ripeti l'"order by", la where condition e metti un "where 1 = 1".

    HTH


    Alberto Dallagiacoma
    My Italian Blog: http://blogs.ugidotnet.org/alby
    Twitter: http://twitter.com/albertodall
    DotDotNet - User Group .NET Emilia Romagna: http://www.dotdotnet.org


    mercoledì 9 ottobre 2013 15:01
  • ok grazie 1000!

    Avevi ragione c'è un order by in di troppo :)


    Diego Riccardi

    giovedì 10 ottobre 2013 09:12