L'AI dove vivono i dati aziendali (parte 3): Ricerca semantica con T-SQL
Punto di partenza
Nel secondo articolo di questa serie abbiamo creato un external model verso Azure OpenAI e abbiamo usato la funzione AI_GENERATE_EMBEDDINGS per generare gli embeddings dei testi presenti nella tabella ai_demo.PostSearchDocuments.
La tabella ai_demo.PostSearchDocuments nel database StackOverflowMini contiene ora due elementi essenziali:
- I dati grezzi, come titolo, tag, testi delle domande e delle risposte
- La colonna
Embedding, che contiene la rappresentazione vettoriale del testo costruito nella colonnaDocumentText
In questo terzo articolo useremo finalmente gli embeddings per eseguire ricerche semantiche direttamente da T-SQL. L’obiettivo è trovare testi simili per significato rispetto a una richiesta espressa in linguaggio naturale, anche quando il testo cercato non coincide esattamente con le parole presenti nei documenti.
Il meccanismo sarà il seguente: trasformeremo il testo della ricerca in un vettore usando lo stesso modello di embedding già configurato, poi confronteremo quel vettore con quelli salvati nella tabella usando la funzione VECTOR_DISTANCE.
Dal testo alla distanza vettoriale
La ricerca semantica parte da una richiesta espressa in linguaggio naturale. Invece di cercare una corrispondenza esatta tra parole, trasformiamo quella richiesta in un embedding usando lo stesso modello utilizzato per generare gli embeddings dei testi salvati nella tabella ai_demo.PostSearchDocuments; avremo quindi due elementi confrontabili:
- Il vettore generato a partire dal testo che si desidera cercare
- I vettori già presenti nella colonna
Embedding
La funzione VECTOR_DISTANCE permette di calcolare la distanza tra questi vettori. Nel nostro esempio useremo la metrica cosine, molto comune negli scenari di ricerca semantica.
Il valore restituito rappresenta una distanza: più il valore è basso, cioè più vicino a zero, più il testo salvato nella tabella ai_demo.PostSearchDocuments è vicino semanticamente al testo cercato. Ordinando i risultati per distanza crescente possiamo quindi ottenere i documenti più simili per significato.
Ricerca semantica con VECTOR_DISTANCE
Il sistema è pronto per ricevere richieste espresse in linguaggio naturale. In questo esempio non cerchiamo direttamente le parole “parameter sniffing”, ma descriviamo un problema tipico: una stored procedure diventa lenta solo per alcuni valori dei parametri e sembra riutilizzare un piano di esecuzione non ottimale.
-- Semantic search with VECTOR_DISTANCE
DECLARE
@SearchText NVARCHAR(MAX) = N'After a deployment, the same stored procedure ' +
'became slow only for some parameter values. ' +
'It looks like SQL Server is reusing a bad execution plan.';
DECLARE
@qv VECTOR(1536) = AI_GENERATE_EMBEDDINGS
(
@SearchText
USE MODEL [AzureOpenAI_text_embedding_ada_002]
);
SELECT
TOP (10)
VECTOR_DISTANCE('cosine', @qv, Embedding) AS Distance
,QuestionId
,AcceptedAnswerId
,QuestionScore
,AcceptedAnswerScore
,ViewCount
,Tags
,Title
FROM
[ai_demo].[PostSearchDocuments]
WHERE
Embedding IS NOT NULL
ORDER BY
Distance;
GO
La variabile @SearchText contiene il testo della ricerca. Questo testo viene trasformato in un vettore tramite la funzione AI_GENERATE_EMBEDDINGS, usando lo stesso external model utilizzato nella fase di generazione degli embeddings.
La query confronta poi il vettore appena generato con la colonna Embedding della tabella ai_demo.PostSearchDocuments, calcolando la distanza tramite VECTOR_DISTANCE. I risultati vengono ordinati per distanza crescente, in modo da mostrare per primi i documenti semanticamente più vicini alla richiesta.
Si osserva che la ricerca non dipende dalla presenza esatta delle parole usate nel testo della richiesta. Il motore può recuperare contenuti collegati al problema descritto, anche se nei testi salvati vengono usati termini differenti o formulazioni diverse.
Ricerca semantica e filtri relazionali
La ricerca semantica può essere combinata con i normali predicati T-SQL. Questo è uno degli aspetti più interessanti dell’approccio: il confronto vettoriale permette di trovare testi simili per significato, mentre il modello relazionale continua a essere usato per applicare filtri, ordinamenti e criteri di qualità sui dati.
Nel seguente esempio cerchiamo contenuti relativi all’aggiornamento di righe in una tabella SQL Server usando valori provenienti da un’altra tabella. Oltre alla distanza vettoriale, applichiamo anche un filtro sul punteggio della domanda, considerando solo documenti con QuestionScore >= 10.
-- Semantic search with VECTOR_DISTANCE and relational filtering
DECLARE
@SearchText NVARCHAR(MAX) = N'I need to update rows in one SQL Server table ' +
'using values from another table joined by a key.';
DECLARE
@qv VECTOR(1536) = AI_GENERATE_EMBEDDINGS
(
@SearchText
USE MODEL [AzureOpenAI_text_embedding_ada_002]
);
SELECT
TOP (10)
VECTOR_DISTANCE('cosine', @qv, Embedding) AS Distance
,QuestionId
,AcceptedAnswerId
,QuestionScore
,AcceptedAnswerScore
,ViewCount
,Tags
,Title
,LEFT(AcceptedAnswerBody, 1000) AS AcceptedAnswerPreview
FROM
[ai_demo].[PostSearchDocuments]
WHERE
Embedding IS NOT NULL
AND QuestionScore >= 10
ORDER BY
Distance;
GO
La condizione QuestionScore >= 10 non partecipa al calcolo della distanza semantica, ma restringe l’insieme delle righe restituite ai soli contenuti che hanno già ricevuto un certo riconoscimento dalla community.
In uno scenario aziendale lo stesso principio può essere applicato usando metadati diversi, ad esempio area applicativa, prodotto, lingua, data di aggiornamento, stato del documento o livello di confidenzialità. La ricerca semantica individua i contenuti più vicini per significato, mentre i filtri relazionali permettono di mantenere il controllo sui dati da includere nei risultati.
Uno sguardo al piano di esecuzione
Osservando il piano di esecuzione della query precedente si nota un comportamento importante. La ricerca con VECTOR_DISTANCE viene eseguita calcolando la distanza tra il vettore della richiesta e gli embeddings presenti nelle righe candidate.
SQL Server esegue una scansione della tabella ai_demo.PostSearchDocuments, applica i predicati Embedding IS NOT NULL e QuestionScore >= 10, calcola la distanza vettoriale con l’operatore Compute Scalar e ordina il risultato con un Top N Sort per restituire le prime 10 righe.
La scansione legge tutte le righe della tabella: non c’è un indice sulla colonna QuestionScore; i predicati riducono l’insieme a 1.487 righe e su queste viene calcolata la distanza con VECTOR_DISTANCE. Il risultato viene poi ordinato per distanza crescente.
Il funzionamento della ricerca semantica con la funzione VECTOR_DISTANCE è chiaro: ogni riga candidata viene confrontata con il vettore della richiesta e i testi semanticamente più vicini vengono restituiti per primi. Su un insieme di dati contenuto questo approccio performa bene. Quando il numero di righe cresce, però, diventa importante valutare meccanismi più adatti alla ricerca vettoriale su volumi maggiori. È qui che entrano in gioco gli indici vettoriali e la funzione VECTOR_SEARCH, che approfondiremo nel prossimo articolo.
Conclusioni
In questo articolo abbiamo usato gli embeddings generati precedentemente per eseguire ricerche semantiche direttamente da T-SQL. Abbiamo trasformato una richiesta espressa in linguaggio naturale in un vettore e l’abbiamo confrontata con gli embeddings salvati nella tabella ai_demo.PostSearchDocuments usando la funzione VECTOR_DISTANCE.
La ricerca semantica non sostituisce il modello relazionale, ma lo affianca. I predicati T-SQL permettono di applicare filtri sui dati, mentre la distanza vettoriale consente di ordinare i risultati in base alla vicinanza semantica rispetto alla richiesta dell’utente.
Nel prossimo articolo vedremo come creare un indice vettoriale e come usare VECTOR_SEARCH per eseguire ricerche semantiche con SQL Server 2025 e Azure SQL.
