Degrado prestazioni query: frammentazione indici o chiavi

di il
14 risposte

Degrado prestazioni query: frammentazione indici o chiavi

Buongiorno.
Mi trovo di fronte ad uno "strano" problema di prestazioni in merito ad una query. Cerco di spiegare bene il contesto.
Ho una web-application (un sito e-commerce) che permette all'utente collegato di effettuare una ricerca all'interno del catalogo articoli.
Esistono diverse tipologie di utente e ciascuno ha una visibilità più o meno ampia del catalogo (che può variare nel tempo).
Per "pilotare" la visibilità degli articoli, uso una tabella (Filtro) dove, in fase di login, vengono inseriti per l'utente l'elenco completo degli articoli che lui può vedere. Poi, nelle varie query, userò questa tabella per "filtrare" i risultati delle ricerche e di altre operazioni che possono essere effettuate (l'utente "standard" può vedere circa 10.000 articoli su un catalogo totale di quasi 60.000, non so se questa possa essere un'informazione utile).
Le uniche operazioni che modificano i dati all'interno della tabella Filtro sono:
- La login (che elimina preventivamente tutti gli articoli presenti per l'utente e poi la ripopola in base alle condizioni attuali)
- La logout (che elimina tutti gli articoli dell'utente, se è il caso [c'è tutto un controllo su eventuali sessioni multiple, non mi dilungo])
- La scadenza della sessione (che è esattamente come una logout)

Passiamo alla struttura delle tabelle coinvolte nel problema.

Articolo:
- ID INT (Primary Key)
- codiceNuovo VARCHAR(18)
- descrizione VARCHAR(80)
- ... altri dati su cui non mi dilungo

Filtro:
- idUtente INT (Primary Key)
- codiceNuovo VARCHAR(18) (PrimaryKey)
- ... altri dati su cui non mi dilungo

Disponibilita:
- codiceNuovo VARCHAR(18) (Primary Key)
- giacenza INT
- impegnato INT
- ... altri dati su cui non mi dilungo

La tabella Articolo possiede un campo ID (chiave primaria) per ragioni storiche (è una tabella che è nata su MySQL, che serviva ad altri scopi, che poi è stata importata così com'è su SQL Server per ragioni che non sto qui a spiegare). In realtà il campo ID sarebbe assolutamente superfluo in quanto il campo "codiceNuovo" da solo sarebbe già una chiave (tant'è che questo campo è indicizzato, per ovvie ragioni). Il campo descrizione, assieme ad altri campi, è incluso in un indice FullText.

Della tabella Filtro ho già parlato: qui dentro vengono inseriti tutti gli articoli "visibili" dal singolo utente (la chiave primaria, infatti, è composta dall'idUtente e il codice articolo).

Non credo ci sia nulla da dire sulla tabella Disponibilità visto che, dai test, non rientra nelle concause del problema.

Problema: ogni tanto gli utenti si lamentano che le ricerche "non funzionano" (non producono risultati). Ho, quindi, fatto diversi test e ho constatato che la query di ricerca ha dei problemi di performance: ogni tanto l'esecuzione della query impiega davvero troppo tempo, questo fa scattare il timeout che ho imposto per l'esecuzione della query nella webapp e, di conseguenza, il risultato all'utente è nullo (in realtà, la query produce dei risultati, ma decisamente troppo tardi).

Vediamo la query ("pippo" è ovviamente un dato variabile e <id dell'utente> ce l'ho in sessione):

SELECT TOP 360 A.ID, A.codiceNuovo, A.descrizione, (D.giacenza - D.impegnato) AS disponibile, ...
FROM Articolo A
    LEFT JOIN CONTAINSTABLE(Articolo, *, 'FORMSOF(INFLECTIONAL, "pippo") OR FORMSOF(THESAURUS, "pippo")') AS Key_TBL ON A.ID = Key_TBL.[KEY] 
    INNER JOIN Filtro F ON (A.codiceNuovo = F.codiceNuovo AND F.idUtente = <id dell'utente>) 
    LEFT JOIN Disponibilita D ON (A.codiceNuovo = D.codiceNuovo) 
WHERE CONTAINS((A.descrizione, A.Titolo, <altri campi dell'indice full-text>), '"pippo*"')
ORDER BY Key_TBL.RANK DESC
In condizioni normali questa query viene eseguita in circa 5 secondi. Nei casi in cui l'utente lamenta il problema, la stessa identica query impiega circa 45 secondi per ritornare il risultato. (tutti i test li ho eseguiti usando SQL Server Management Studio, quindi non dipendono dalla WebApp).

Quando mi sono trovato nella situazione descritta (query che impiega più di 40 secondi), ho provato a "togliere di mezzo" la tabella del filtro (come si può vedere è in INNER JOIN per poter "filtrare" i risultati in base alla visibilità dell'utente): come per magia la prestazione torna ai massimi livelli (meno di 5 secondi).

Analizzando la situazione ho notato che il problema sparisce (la query ritorna assolutamente prestante) se eseguo una ricostruzione (o una riorganizzazione) dell'indice della tabella Filtro.

ALTER INDEX ALL ON Filtro REORGANIZE
(posso usare anche REBUILD)

Ad occhio, quindi, sembrerebbe un problema di frammentazione dell'indice di quella tabella (ci può stare vista la quantità di record che vengono inseriti ed eliminati quotidianamente, anche nell'arco di una sola ora). La tabella Filtro può variare, infatti, come dimensione da 0 record a poco più di 1.000.000 in base alla quantità e tipologia di utenti che sono mediamente collegati.
Mi sarei aspettato, comunque, che il problema si presentasse in presenza di una forte frammentazione... invece noto che il problema si presenta anche con una frammentazione inferiore al 2%.

Per controllare la frammentazione dell'indice mi sono servito della query messa a disposizione dal MSDN:

SELECT a.object_id,
       object_name(a.object_id) AS TableName,
       a.index_id,
       name AS IndedxName,
       avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID ('MioDB')
        , OBJECT_ID('Filtro')
        , NULL
        , NULL
        , NULL) AS a
INNER JOIN sys.indexes AS b ON (a.object_id = b.object_id AND a.index_id = b.index_id)
A questo punto mi si sono prospettate due possibili strade:
1) Effettuare una riorganizzazione/ricostruzione dell'indice della tabella Filtro con una certa frequenza
2) Provare a modificare la tabella Articolo, togliendo di mezzo il campo ID (inutile), facendo diventare chiave primaria il campo "codiceNuovo"

Non so se la 2 possa portare qualche miglioria (chiedo a voi)
La 1) non mi piace, ma se serve la posso attuare molto facilmente.

Voi avete qualche suggerimento?

Grazie per l'attenzione posta fin qui (mi rendo conto di aver scritto un pippone, ma ho voluto dare più dettagli possibili).

14 Risposte

  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Mi sono accorto di aver tralasciato qualche dettaglio (magari non serve).

    Viene usato SQL Server 2008 R2 (è previsto tra qualche mese il passaggio alla versione 2019, ma non ci conto tanto).

    SQL Server gira su un server dedicato Microsoft Windows Server 2008 R2 64 bit - 8 core - 24 GB Ram. Anche questo a breve dovrebbe essere sostituito con una versione più recente.
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Non serve leggere tutti i dettagli, anche perche' i dettagli forniti NON SONO completi, per sapere a che cosa sono dovuti I problemi prestazionali di una query.

    I motivi sono sempre gli stessi e la soluzione anche:

    0) il problema NON E' dove uno PENSA che sia
    1) ricerca di QUALE query presenta problemi: non saranno TUTTE, ma alcune
    2) analisi mediante il query plan per capire PERCHE' la query e' lenta
    3) riformulazione della query/creazione di indici per velocizzare la query

    Da non trascurare analisi dell'uso di ram & disco da parte di SQL Server.

    Tutte cose che vanno analizzate 'in sito'.
    Benche' la soluzione 'generale' sia 'concettualmente' semplice, la sua applicazione 'specifica' non lo e' e potrebbe richiedere la riscrittura di query o la riprogettazione di parte del database.

    Nota: gia' leggendo le prime righe del post ci sono descrizioni di soluzioni decisamente 'strane' (popolare e rimuovere contenuti di tabelle per ogni login/logout? Mah!).

    Soluzione: contatta qualche 'vero esperto' di progettazione di database/'vero progettista software'
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Grazie migliorabile. Cercherò di rispondere più dettagliatamente ai punti che hai fornito.

    migliorabile ha scritto:


    Non serve leggere tutti i dettagli, anche perche' i dettagli forniti NON SONO completi, per sapere a che cosa sono dovuti I problemi prestazionali di una query.
    Se servono ulteriori dettagli in merito al problema, sono qui.
    0) il problema NON E' dove uno PENSA che sia
    Corretto. Ma nello specifico, a far scaturire il problema è SOLO quella query. E il problema si presenta sia all'interno della webapp, sia eseguendola manualmente su SSMS, sia da un altro software di SQL Editing indipendente, ma solo quando la frammentazione dell'indice sale. Ricostruendo l'indice il problema SPARISCE immediatamente. 3 indizi non faranno una prova, ma danno l'idea di quale sia la parte in cui guardare.
    1) ricerca di QUALE query presenta problemi: non saranno TUTTE, ma alcune
    Solo quella di cui ho parlato. Non ci sono altre query. L'unico problema sta nella ricerca (esecuzione di QUELLA query) e l'ho verificato tramite diversi test, come ho scritto.
    2) analisi mediante il query plan per capire PERCHE' la query e' lenta
    Fatta. Il query plan non identifica alcun problema. Non suggerisce alcun miglioramento per la query in questione, non lamenta alcun indice mancante o roba simile... in altre situazioni l'ho proficuamente usato per analizzare altre query e, dove necessario, ha trovato indici mancanti o suggerito migliorie... non in questo caso.
    3) riformulazione della query/creazione di indici per velocizzare la query
    E' quello che mi aspetto, infatti. La query è frutto di mesi di lavoro di più persone, me compreso, che l'hanno via via alleggerita, migliorata e resa performante. Solo quando la frammentazione di quell'indice sale, il problema si verifica. Per questo ho inizialmente puntato il dito verso quell'indice. Ma è davvero banale: una chiave primaria composta di quei due campi. Null'altro. E solo quei due campi sono coinvolti nella query.
    Da non trascurare analisi dell'uso di ram & disco da parte di SQL Server.
    Come tutti sappiamo, SQL Server si prende TUTTA la RAM disponibile e non la molla... questo non è un problema, la macchina è dedicata a lui. Il disco non è un problema... la macchina è virtuale e ogni volta che serve lo si allarga. Al momento monta 3 dischi: uno dedicato al sistema operativo (80 GB di cui 10 ancora liberi; uno dedicato ai DB: 300 GB di cui 60 GB ancora liberi; uno dedicato ai al deposito in locale dei backup notturni, che poi vengono spostati su NAS: 250 GB di cui 50 GB ancora liberi). SQL Server non ospita solo il DB dedicato a questa webapp... ha anche altri DB che servono altri software. Non presenta alcun problema prestazionale da nessuna parte... solo nell'eseguire QUESTA SPECIFICA query.
    Nota: gia' leggendo le prime righe del post ci sono descrizioni di soluzioni decisamente 'strane' (popolare e rimuovere contenuti di tabelle per ogni login/logout? Mah!).
    L'ho scritto piuttosto chiaramente il motivo, non credo serva dilungarsi oltre. Vedila un po' come una "tabella temporanea", anche se così non è.
    Soluzione: contatta qualche 'vero esperto' di progettazione di database/'vero progettista software'
    Già...
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Ciao, sembra che il tuo sia un problema di fragmentation viste le continue modifiche quotidiane a DB. Potresti come hai pensato far partire periodicamente un custom script per riorganizzare index fragmentation :

    https://www.mssqltips.com/sqlservertip/4470/script-to-manage-sql-server-rebuilds-and-reorganize-for-index-fragmentation/

    Non sono un esperto Microsoft SQL, ti rimando a questi articoli :

    https://www.mssqltips.com/sql-server-tip-category/39/fragmentation-and-index-maintenance/
    https://ola.hallengren.com
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    5 secondi per una query e' IMPROPONIBILE. Non e' plausibile NEMMENO con tabelle da milioni di record.

    In applicazioni real-time, QUALUNQUE query sopra il secondo va RICONSIDERATA.

    Query che necessitano di tanto tempo hanno senso SOLO in operazioni batch.

    (Se poi 'non se po' fa, non se po fa'! Ma deve essere un 'SE' ENORME)

    Mi sono letto un po' meglio il post: e' EVIDENTE che l'utilizzo della tabella Filtro, che viene popolata e vuotata ad ogni login/logout (e che ho trovato strano fin dall'inizio), e per la quale c'e' un indice che non viene aggiornato abbastanza di frequente,

    E' il collo di bottiglia.

    Devi trovare una soluzione alternativa: popolare e vuotare una tabella in continuazione, con dei dati che poi sono sempre gli stessi (immagino che ad ogni login dello stesso utente verranno inseriti sempre gli stessi dati), a meno di non avere un numero DECISAMENTE limitato di entry, non ha senso.

    Ad esempio:
    la tabella Filtro deve gia' ESISTERE, con i filtri associati a TUTTI gli utenti (ogni filtro ha il suo idutente).

    Alla login conosci l'id dell'utente, con questo id selezioni I filtri e fai il join con il resto.

    La tabella Filtri la aggiorni SOLO a fronte di aggiornamenti sugli utenti e prodotti, e ti assicuri di aggiornare anche gli indici. Questa operazione DEVE essere MENO frequente e con modifiche PIU' LIMITATE che non l'attuale utilizzo.

    Probabilmente si puo' fare di meglio.

    (La query continua a non convincermi: ci sono pezzi fulltext, con join relazionali, una orderby ed la selezione di 360 record. Ad esempio, l'orderby su QUANTI record agisce? Ha poco senso ordinare, che ne so, 100.000 record per poi estrarne 360. Boh.
    Perche' usare un indice fulltext multicampo piuttosto che usare un campo singolo, contenente tutto il testo del record? Ci sono differenze di efficienza? Mah!)
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Salve a tutti,
    il pop del post l'ha portato in alto

    SpiritoLibero ha scritto:


    Nota: gia' leggendo le prime righe del post ci sono descrizioni di soluzioni decisamente 'strane' (popolare e rimuovere contenuti di tabelle per ogni login/logout? Mah!).
    L'ho scritto piuttosto chiaramente il motivo, non credo serva dilungarsi oltre. Vedila un po' come una "tabella temporanea", anche se così non è.
    anche per me, come per @migliorabile, questo scenario non ha molto senso... se popoli una tabella di appoggio, allora quell'operazione probabilmente e' possibile direttamente interrogando la base dati sottostante senza il bisogno di serializzarla, visto che
    
    INNER JOIN Filtro F ON (A.codiceNuovo = F.codiceNuovo AND F.idUtente = <id dell'utente>) 
    
    e' sicuramente sostituibile dalla query che ha popolato {Filtro}...

    cosi' a spanna lo vedo come code smell


    saluti omnia
    --
    Andrea
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    migliorabile ha scritto:


    5 secondi per una query e' IMPROPONIBILE. Non e' plausibile NEMMENO con tabelle da milioni di record.
    Ti garantisco che non è così. La query che ho postato è una MINIMA parte di quello che viene realmente fatto: in realtà alcuni dei campi che vengono estratti sono l'elaborazione contemporanea di diverse Stored Function che servono per applicare algoritmi di logica di business che, gioco forza, rallentano la query. Il tutto è ASSOLUTAMENTE normale e accettato per il tipo di ricerca effettuata e l'ambito (che, per ovvie ragioni, non sto qui a esplicare). Sta di fatto che non sono quelle a provocare il problema, dato che sono indipendenti dalla tabella di Filtro.
    QUALUNQUE query sopra il secondo va riconsiderata.
    Query che necessitano di tanto tempo hanno senso SOLO in operazioni batch.
    Tralasciamo questi discorsi.
    Mi sono letto un po' meglio il post: e' EVIDENTE che l'utilizzo della tabella Filtro, che viene popolata e vuotata ad ogni login/logout (e che ho trovato strano fin dall'inizio), e per la quale c'e' un indice che non viene aggiornato abbastanza di frequente, E' il collo di bottiglia.
    Sì, fin qua l'ho visto anch'io.
    Devi trovare una soluzione alternativa: popolare e vuotare una tabella in continuazione, a meno di non avere un numero DECISAMENTE limitato di entry, non ha senso.
    Può sembrare senza senso, ma ti garantisco che ne ha: la situazione degli articoli (60.000 referenze) può cambiare ogni minuto: parlo di giacenze, modifiche all'anagrafica, modifiche ai listini (personalizzati per ciascun utente, sì), più altre variabili che determinano se un determinato articolo può essere visto o meno da un particolare utente. E' IMPENSABILE avere già i filtri pronti su un bacino di oltre 10.000 utenti (stiamo parlando di centinaia di milioni di record, che possono variare TUTTI con una frequenza di circa 30 minuti quando va bene, fai tu). Tutte queste informazioni che provengono da sistemi informativi diversi (ed eterogenei) farebbero collassare la rete aziendale. Per questo ha senso elaborarle SOLO quando servono e solo per chi servono: cioè quando l'utente si logga.
    Ad esempio:
    la tabella Filtro deve gia' ESISTERE, con i filtri associati a TUTTI gli utenti (ogni filtro ha il suo idutente). NON LA POPOLI al login (non ha senso)
    Ne ha, ne ha.
    La tabella Filtri la aggiorni SOLO a fronte di aggiornamenti sugli utenti e prodotti, e ti assicuri di aggiornare anche gli indici. Questa operazione DEVE essere MENO frequente e con modifiche PIU' LIMITATE che non l'attuale utilizzo
    Vedi quanto detto sopra: MENO frequente e PIU' LIMITATE significa proprio SOLO quando serve... cioè quando l'utente fa la login. Non ogni 30 minuti.

    @asql: non è così... chi popola quella tabella NON è una query, ma una routine che applica le logiche aziendali. Una sequenza di operazioni, calcoli, che determinano se un determinato articolo debba o meno far parte di quel filtro. Impensabile applicarla ad ogni operazione successiva. Il popolamento di quella tabella può richiedere diversi secondi (sono MOLTE le regole da applicare in cascata), per questo meglio spenderla una tantum in fase di login e avere già i dati pronti per tutto il resto della navigazione dell'utente.Per dire, fino a qualche anno fa il riempimento di quella tabella veniva demandato ad un programma esterno (chiamato in CGI).
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    10.000 utenti sono 'bazzeccole'
    60.000 articoli sono 'quisquiglie'
    per un dbms come SQL Server

    Una tabella di qualche decina di milioni di record, con un indice ben fatto, da cui estrai 60.000 record (con record di dimensioni microsocpiche - ad esempio composte da 2 ID), COMUNQUE la interroghi in millisecondi.

    Mappare OGNI utente con una configurazione TOTALMENTE diversa di regole di visibilita' per gli articoli mi pare ALQUANTO strano.
    Non ci credo che non esista qualche 'ragionevole' meccanismo di 'classificazione' lato utent & lato prodotti!

    Ci sono siti che processano centinaia di migliaia di utenti al secondo su milioni di articoli con query dell'ordine dei millisecondi: NON E' solo questione di hardware.

    Se una query richiede 5 secondi, vuol dire che ti serve un computer 1000 volte piu' veloce per avere una query da 5 millisecondi. Oppure 1000 computer. Assurdo.

    C'e' qualcosa a MONTE che e' sbagliato.
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Se rifare l'indice ti risolve parzialmente il problema ti direi di farlo fino a quando non trovi il bandolo della matassa anche se la frammentazione al 2% non dovrebbe creare problemi ( https://docs.microsoft.com/it-it/sql/relational-databases/indexes/reorganize-and-rebuild-indexes?view=sql-server-2017 ) , se però un utente si connette alle 9 del mattino ed i dati cambiano alle 10 continua a vedere i dati già calcolati se è nella stessa "sessione"?
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Ciao,
    fino all'anno scorso gestivo un comparatore di prezzi per gli acquisti online, con un catalogo medio di offerte di 10 mln di item e con una frequenza di variazione del 20% ogni ora. Il sistema era velocissimo nonostante girasse su una macchina virtuale veramente economica.

    A confronto l'hardware che hai a disposizione è di super lusso. Non hai detto comunque se il sistema usa dischi ssd.

    Probabilmente il problema si verifica quando più utenti effettuano il login contemporaneamente. In quel caso il sistema è costretto a gestire un'importante attività di modifica ai dati e indice della tabella filtro.

    Hai scartato l'ipotesi di mantenere in memoria la tabella filtro che mi sembra essere una tabella di mapping utente-prodotti.
    Tramite Redis o altro sistema non gestito dal DBMS?

    Faresti una prima query sugli articoli senza il filtro utente (60 k di item sono poca roba ) successivamente filtri i risultati in base all'associazione utente-prodotti.
    Puoi farglieli filtrare al DBMS passando il filtro tramite un
    tipo di dato custom (tabella) oppure esternamente dal tuo software.
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Ho un aggiornamento e alcune precisazioni.
    L'altro giorno è stata davvero una giornata pesante e rileggendo quel che ho scritto mi sono accorto di alcune inesattezze.

    1) Ho scritto più volte che la query in condizioni normali impiega circa 5 secondi... alla faccia del lapsus: 0.049s sono 5 CENTESIMI di secondo; a volte la pressione e le sensazioni non ti fanno scrivere quello che stai pensando.

    2) Da ulteriori test SEMBREREBBE (devo usare il condizionale) che il problema si presenti solo in presenza di una PRECISA richiesta da parte dell'utente, non da TUTTE le ricerche (è comunque strano perchè la richiesta consta di una banalissima parola in Italiano, niente di ché). Ho pensato all'indice full-text, gliel'ho fatto ricostruire, ma non sembra dipendere da quello.

    Oggi siamo giunti ad una modifica della query che, in barba a tutte le teorie, oltre a non presentare il problema, sembra essere (parola del piano di esecuzione di SQL Server) "migliore" dell'altra in termini di costo percentuale.
    
    SELECT X.*
    FROM (
       SELECT TOP 360 A.ID, A.codiceNuovo, A.descrizione, (D.giacenza - D.impegnato) AS disponibile, ..., Key_TBL.RANK AS MyRank
       FROM Articolo A
           LEFT JOIN CONTAINSTABLE(Articolo, *, 'FORMSOF(INFLECTIONAL, "pippo") OR FORMSOF(THESAURUS, "pippo")') AS Key_TBL ON A.ID = Key_TBL.[KEY] 
           LEFT JOIN Disponibilita D ON (A.codiceNuovo = D.codiceNuovo) 
       WHERE CONTAINS((A.descrizione, A.Titolo, <altri campi dell'indice full-text>), '"pippo*"')
    ) X
    WHERE X.codiceNuovo IN (
       SELECT codiceNuovo
       FROM Filtro
       WHERE idUtente = <id dell'utente>
    )
    ORDER BY X.MyRank DESC
    
    Questa è semanticamente equivalente all'altra, ma non soffre dello stesso problema: ho sostituito la JOIN sul Filtro con una WHERE con clausola IN e una sub-qeury sulla stessa tabella Filtro.
    Mi hanno sempre detto che una INNER JOIN è da preferire (ove possibile) all'uso di una WHERE con clausola IN, ma a quanto pare il piano d'esecuzione di SQL Server non è d'accordo.

    Sto monitorando la situazione con questa nuova query e, al momento, non ho nessuna avvisaglia di problemi.
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Salve a tutti,
    giusto per caso, RedGate ha ripubblicato oggi un link al download gratuito di un eBook relativo a Performance Tuning che avevo gia' scaricato ma non ricordavo di avere...
    https://www.red-gate.com/library/performance-tuning-with-sql-server-dynamic-management-views

    saluti omnia
    --
    Andrea
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    L'ho scaricato.
    Sarà un'ottima lettura.
    Grazie.
  • Re: Degrado prestazioni query: frammentazione indici o chiavi

    Ciao SpiritoLibero,
    sono incappato in questo thread per caso ma volevo solo segnalarti che le due query possono restituire risultati molto diversi.

    La prima quella originale ritorna le prime 360 ROWS (ordinate per RANK DESC) tra tutte le righe che corrispondono alla condizione di ricerca e che siano comprese nella tabella filtro degli articoli validi x l'utente.
    
    SELECT TOP 360 A.ID, A.codiceNuovo, A.descrizione, (D.giacenza - D.impegnato) AS disponibile, ...
    FROM Articolo A
        LEFT JOIN CONTAINSTABLE(Articolo, *, 'FORMSOF(INFLECTIONAL, "pippo") OR FORMSOF(THESAURUS, "pippo")') AS Key_TBL ON A.ID = Key_TBL.[KEY] 
        INNER JOIN Filtro F ON (A.codiceNuovo = F.codiceNuovo AND F.idUtente = <id dell'utente>) 
        LEFT JOIN Disponibilita D ON (A.codiceNuovo = D.codiceNuovo) 
    WHERE CONTAINS((A.descrizione, A.Titolo, <altri campi dell'indice full-text>), '"pippo*"')
    ORDER BY Key_TBL.RANK DESC
    
    La seconda ritorna una sotto query con le prime 360 ROWS a caso (ordinamento perso) che corrispondono alla condizione di ricerca, e poi filtra sulla base della tabella degli Articoli validi x l'utente.
    Ora facciamo il caso estremo che nelle TOP 360 non ci sia nessun articolo valido (final result: 0 row(s) affected)... ma nessuno e dico nessuno ti garantisce che dalla riga 361 in poi della sotto query non possano essere presenti degli articoli validi.

    Quindi per renderle uguali dovresti far tornare tutte le righe dalla sotto query e poi fare fuori la SELECT TOP 360 X.* ordinata, ed allora magari con un result set della sotto query più grande di sole 360 righe potresti ritrovarti nuovamente con il problema.
    
    SELECT X.*
    FROM (
       SELECT TOP 360 A.ID, A.codiceNuovo, A.descrizione, (D.giacenza - D.impegnato) AS disponibile, ..., Key_TBL.RANK AS MyRank
       FROM Articolo A
           LEFT JOIN CONTAINSTABLE(Articolo, *, 'FORMSOF(INFLECTIONAL, "pippo") OR FORMSOF(THESAURUS, "pippo")') AS Key_TBL ON A.ID = Key_TBL.[KEY] 
           LEFT JOIN Disponibilita D ON (A.codiceNuovo = D.codiceNuovo) 
       WHERE CONTAINS((A.descrizione, A.Titolo, <altri campi dell'indice full-text>), '"pippo*"')
    ) X
    WHERE X.codiceNuovo IN (
       SELECT codiceNuovo
       FROM Filtro
       WHERE idUtente = <id dell'utente>
    )
    ORDER BY X.MyRank DESC
    
    Consiglierei di mettere un (nolock) dopo la tabella Filtro almeno mentre leggi sei sicuro che "se i dati del utente non cambiano" eviti di avere blocchi sulla lettura dovuti alle fasi di scrittura per login/logout di altri utenti.
    Saluti
    Alberto
Devi accedere o registrarti per scrivere nel forum
14 risposte