{"id":111121,"date":"2026-06-17T12:00:00","date_gmt":"2026-06-17T12:00:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=111121"},"modified":"2026-06-17T12:29:27","modified_gmt":"2026-06-17T12:29:27","slug":"vector-search-in-sql-server-vector_distance-vector_search-and-index-trade-offs","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/vector-search-in-sql-server-vector_distance-vector_search-and-index-trade-offs\/","title":{"rendered":"Vector search in SQL Server: VECTOR_DISTANCE, VECTOR_SEARCH, and index trade-offs"},"content":{"rendered":"\n<p><strong>In this article, I&#8217;ll explain how to implement vector search in SQL Server using the <code>VECTOR_DISTANCE<\/code> and <code>VECTOR_SEARCH<\/code> functions. You&#8217;ll learn how vector indexes work, when to use them, and what the key trade-offs are \u2014 including the current read-only table limitation. I&#8217;ll also cover the latest syntax changes in Azure SQL Database, where that limitation has already been lifted.<\/strong><\/p>\n\n\n\n<p><strong><em>This is the third article in Greg Low\u2019s series&nbsp;<a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/ai-text-embeddings-in-sql-server-everything-you-need-to-know\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u2018AI text embeddings in SQL Server: everything you need to know\u2019.<\/a><\/em><\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-vector-distance-function-in-sql-server\">The VECTOR_DISTANCE function in SQL Server<\/h2>\n\n\n\n<p>Like how <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/how-to-host-an-ai-text-embeddings-model-for-sql-server-using-ollama\/\" target=\"_blank\" rel=\"noreferrer noopener\">text embeddings<\/a> are a representation of a location in a number space, any text that is similar in meaning to one another is located close together.<\/p>\n\n\n\n<p>For example, I have a table of products with many columns but, for our purposes, the key ones are <code>ProductID<\/code> as an integer <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/primary-key-primer-for-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">primary key<\/a>, and a <code>ProductName<\/code> that&#8217;s a <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/when-use-char-varchar-varcharmax\/\" target=\"_blank\" rel=\"noreferrer noopener\">varchar<\/a>(50) column. Here are the first 10 rows of the table:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"940\" height=\"269\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2026\/06\/image-2.png\" alt=\"The first 10 rows of the table.\" class=\"wp-image-111123\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2026\/06\/image-2.png 940w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2026\/06\/image-2-300x86.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2026\/06\/image-2-768x220.png 768w\" sizes=\"auto, (max-width: 940px) 100vw, 940px\" \/><\/figure>\n\n\n\n<p>I&#8217;ll start by adding a column to hold the vector embeddings:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">ALTER TABLE dbo.Products\nADD ProductNameEmbeddings vector(384);\nGO<\/pre><\/div>\n\n\n\n<p>Note that I&#8217;ve used a vector column with 384 dimensions. This is because I&#8217;m going to use the AllMiniLM external model that I created in the last article, and it has 384 dimensions. <\/p>\n\n\n\n<p>Next, I&#8217;ll make sure that both <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/how-to-host-an-ai-text-embeddings-model-for-sql-server-using-ollama\/\" target=\"_blank\" rel=\"noreferrer noopener\">Ollama<\/a> and <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/how-to-host-an-ai-text-embeddings-model-for-sql-server-using-ollama\/#:~:text=My%20preferred%20proxy%20is%20a%20tool%20called%20Caddy.\" target=\"_blank\" rel=\"noreferrer noopener\">Caddy<\/a> are running, and then I&#8217;ll populate this new column by calling the external model:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">UPDATE dbo.Products\nSET ProductNameEmbeddings = AI_GENERATE_EMBEDDINGS(ProductName USE MODEL AllMiniLM);\nGO<\/pre><\/div>\n\n\n\n<p>Once I have the embeddings in place, I can use the <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/an-ai-powered-t-sql-assistant-built-with-python-and-sql-server\/#:~:text=VECTOR_DISTANCE%20is%20a%20function%20that%20accepts%202%20embeddings%20and%20returns%20a%20value%20between%200%20and%202.%20The%20closer%20to%200%2C%20the%20more%20semantically%20similar%20the%20two%20texts%20corresponding%20to%20these%20embeddings%20are.%20This%20metric%20is%20called%20Cosine%20Distance.\" target=\"_blank\" rel=\"noreferrer noopener\"><code>VECTOR_DISTANCE<\/code><\/a> function to check similarity:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DECLARE @UserEmbeddings vector(384) \n  = AI_GENERATE_EMBEDDINGS('Natural tomatoes from Italy' USE MODEL AllMiniLM);\n\nSELECT TOP(5) p.ProductID, p.ProductName \nFROM dbo.Products AS p\nORDER BY VECTOR_DISTANCE('cosine', @UserEmbeddings, p.ProductNameEmbeddings);<\/pre><\/div>\n\n\n\n<p>I started by taking a user prompt &#8211; <strong>Natural tomatoes from Italy<\/strong> &#8211; and calculated embeddings for it. I then used the <code>VECTOR_DISTANCE()<\/code> function to compare that value to the embeddings <strong>for every row in the table<\/strong> based on <a href=\"https:\/\/www.datacamp.com\/tutorial\/cosine-distance\" target=\"_blank\" rel=\"noreferrer noopener\">cosine distance<\/a>. <\/p>\n\n\n\n<p>Note that I could also have used a <a href=\"https:\/\/www.geeksforgeeks.org\/maths\/euclidean-distance\/\" target=\"_blank\" rel=\"noreferrer noopener\">Euclidean <\/a>or dot product based metric, but cosine is perfect for what I&#8217;m doing here. <\/p>\n\n\n\n<p>The values returned on my system were:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"573\" height=\"241\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2026\/06\/image-1.png\" alt=\"\" class=\"wp-image-111122\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2026\/06\/image-1.png 573w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2026\/06\/image-1-300x126.png 300w\" sizes=\"auto, (max-width: 573px) 100vw, 573px\" \/><\/figure>\n\n\n\n<p>That&#8217;s a great response for this query, and note how hard it would have been to get anywhere near that with other functions such as <code>LIKE()<\/code>.<\/p>\n\n\n\n<section id=\"my-first-block-block_4c84236f4826cd9fe49c666011e58f67\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Fast, reliable and consistent SQL Server development&#8230;<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            &#8230;with SQL Toolbelt Essentials. 10 ingeniously simple tools for accelerating development, reducing risk, and standardizing workflows.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/products\/sql-toolbelt-essentials\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Learn more &amp; try for free: Fast, reliable and consistent SQL Server development...\">Learn more &amp; try for free<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-vector-indexes-in-sql-server\">Vector indexes in SQL Server<\/h2>\n\n\n\n<p>While the query I had above is fine for a small number of rows, it would simply be far too compute-intensive with a large number of rows. There are two workarounds for this.<\/p>\n\n\n\n<p>The first is to filter the rows some other way, like just using a standard <a href=\"https:\/\/www.w3schools.com\/sql\/sql_where.asp\" target=\"_blank\" rel=\"noreferrer noopener\">WHERE<\/a> clause. That should always be your first approach.<\/p>\n\n\n\n<p>The second approach is to create a <a href=\"https:\/\/www.yugabyte.com\/key-concepts\/what-is-vector-indexing\/\" target=\"_blank\" rel=\"noreferrer noopener\">vector index<\/a>, for example:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE VECTOR INDEX VX_dbo_Products\nON dbo.Products (ProductNameEmbeddings)\nWITH (\n    metric = 'cosine',\n    type   = 'diskann',\n    maxdop = 4\n);\nGO<\/pre><\/div>\n\n\n\n<p>Creating a vector index is similar to creating other SQL Server indexes, but with the advantage of having a few more options than the others. The main one is the metric. <\/p>\n\n\n\n<p>Since the index is storing a result of a calculation, it needs to know &#8211; at creation time &#8211; which distance <strong>metric<\/strong> will be used. The <strong>type<\/strong> shown here is <code>diskann<\/code> (or disk approximate nearest neighbor). You can also specify the <code>maxdop<\/code><strong> <\/strong>(maximum degrees of parallelism) for the index operation.<\/p>\n\n\n\n<p><strong>The name <code>diskann<\/code> should  hint at something different here. Vector indexes are used to find <em>approximate<\/em> outcomes &#8211; they don&#8217;t guarantee <em>precise<\/em> outcomes. You must keep this in mind when using them.<\/strong><\/p>\n\n\n\n<p>Also note that, at the release of SQL Server 2025, vector indexing was shipped as a <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/sql-server\/preview-features-faq?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">preview feature<\/a>. Therefore, you need to enable preview features on the server to use them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-vector-search-function-in-sql-server\">The VECTOR_SEARCH() function in SQL Server<\/h2>\n\n\n\n<p>It&#8217;s all very well to have this index structure, but how do we use it? The answer is the <code>VECTOR_SEARCH()<\/code> function. Here&#8217;s an example of it in use:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DECLARE @UserEmbeddings vector(384) = AI_GENERATE_EMBEDDINGS('Natural tomatoes from Italy' USE MODEL AllMiniLM);\n\nSELECT TOP(5) p.ProductID, p.ProductName\nFROM VECTOR_SEARCH\n(\n    TABLE = dbo.Products AS p,\n    COLUMN = ProductNameEmbeddings,\n    SIMILAR_TO = @UserEmbeddings,\n    METRIC = 'cosine',\n    TOP_N = 5\n) AS vs\nORDER BY vs.distance;\nGO<\/pre><\/div>\n\n\n\n<p>I&#8217;ve started with the same calculation of text embeddings for a user query about tomatoes. I&#8217;ve then asked for a <code>TOP(5)<\/code> from the vector search function. I tell it the table, column, metric, and <code>TOP_N<\/code> values, along with the value that I want to compare to. <\/p>\n\n\n\n<p>On my system, this returns exactly the same output that the vector distance query did. However, the big difference here is in speed: this method would be drastically faster when dealing with a lot of data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-s-the-problem-with-the-vector-index-in-sql-server\">What&#8217;s the problem with the vector index in SQL Server?<\/h2>\n\n\n\n<p>One thing that might not have been immediately obvious is the effect that creating the vector index had on the table. Unfortunately, when a vector index is present, tables are read-only.<\/p>\n\n\n\n<p>It&#8217;s not a huge issue if the data you are searching is relatively static, but for data that&#8217;s changing, it&#8217;s a problem. And it takes a lot of planning to work around. If you worked with the first version of <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/columnstore-index-improvements-in-sql-server-2025\/\" target=\"_blank\" rel=\"noreferrer noopener\">columnstore indexes<\/a> back in SQL Server 2012, you&#8217;ll understand how challenging this is.<\/p>\n\n\n\n<p>The good news is that Microsoft was aware of how big an issue this would be and was already planning how to fix it. And you&#8217;re likely already seeing the changes if you&#8217;re using <a href=\"https:\/\/www.red-gate.com\/simple-talk\/opinion\/editorials\/how-to-learn-about-azure-sql\/\" target=\"_blank\" rel=\"noreferrer noopener\">Azure SQL Database<\/a>.<\/p>\n\n\n\n<p>At first, the vector indexing in Azure SQL Database had the same limitations as the implementation in SQL Server. And if you have vector indexes that you created in Azure SQL Database earlier, they still apply.<\/p>\n\n\n\n<p>Now, however, if you&#8217;re in a region where the new changes have already been rolled out, things are very different. There&#8217;s a <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/sql-server\/ai\/vectors?view=sql-server-ver17#:~:text=Approximate%20vector%20index%20and%20vector%20search%20are%20in%20preview%20and,SQL%20database%20in%20Microsoft%20Fabric.&amp;text=The%20following%20example%20uses%20the,with%20earlier%20version%20vector%20indexes.\" target=\"_blank\" rel=\"noreferrer noopener\">new version of vector index<\/a>. The syntax is the same, but executing it creates a new type of index. The brilliant part is that the read-only limitation on tables has been removed, too.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-there-s-just-one-catch\">There&#8217;s just one catch<\/h3>\n\n\n\n<p>This is great news but there is one catch. When you have the new style of indexes, you need to use <strong>different syntax<\/strong> for vector search. Let&#8217;s compare the two.<\/p>\n\n\n\n<p>The previous syntax is like what I showed you above:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT TOP (10)\n       p.ProductID,\n       p.[Description]\nFROM VECTOR_SEARCH(\n         TABLE = Sales.Products AS p,\n         COLUMN = DescriptionVector,\n         SIMILAR_TO = @Embeddings,\n         METRIC = 'cosine',\n         TOP_N = 10\n     ) AS vs\nORDER BY vs.distance;<\/pre><\/div>\n\n\n\n<p>And here&#8217;s the new syntax:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT TOP (10) WITH APPROXIMATE\n       p.ProductID,\n       p.[Description]\nFROM VECTOR_SEARCH(\n         TABLE = Sales.Products AS p,\n         COLUMN = DescriptionVector,\n         SIMILAR_TO = @Embeddings,\n         METRIC = 'cosine'\n     ) AS vs\nORDER BY vs.distance;<\/pre><\/div>\n\n\n\n<p>Previously, we had the <code>TOP<\/code> details inside the vector search and in the outer query. Now, it&#8217;s <em>only<\/em> in the outer query, but requires the keywords <code>WITH APPROXIMATE<\/code>.<\/p>\n\n\n\n<p>Under the covers, there&#8217;s a good reason for this change. It&#8217;s mostly about <em>where<\/em> the filtering occurs: whether it&#8217;s during the vector search or only applied later. With the new syntax, it&#8217;s applied <em>within <\/em>the vector search operation &#8211; providing more consistent and faster results.<\/p>\n\n\n\n<p><strong>While we can&#8217;t use these new vector indexes in our on-premises SQL Server systems yet, I&#8217;m sure that a future release will provide similar options to those that now exist in Azure SQL Database.<\/strong><\/p>\n\n\n\n<section id=\"my-first-block-block_e46faba5c2cc4bf29133c6885b720765\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Write accurate SQL faster in SSMS with SQL Prompt AI<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Write or modify queries using natural language, get clear explanations for unfamiliar code, and fix and optimize SQL with ease &#8211; all without leaving SSMS.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/products\/sql-prompt\/#ai-powered-code\" class=\"btn btn--secondary btn--lg\" aria-label=\"Learn more and try for free: Write accurate SQL faster in SSMS with SQL Prompt AI\">Learn more and try for free<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: Vector search in SQL Server<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What is the VECTOR_DISTANCE function in SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p>VECTOR_DISTANCE compares text embeddings to find semantically similar rows, using distance metrics like cosine, Euclidean, or dot product.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. When should I use a vector index in SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Use a vector index when querying large tables \u2014 it makes vector search dramatically faster, though it currently ships as a preview feature in SQL Server 2025 and makes tables read-only.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. What is the VECTOR_SEARCH function and how does it differ from VECTOR_DISTANCE?<\/h3>\n            <div class=\"faq-answer\">\n                <p>VECTOR_SEARCH uses a vector index to return approximate nearest-neighbour results at scale, while VECTOR_DISTANCE scans every row \u2014 making VECTOR_SEARCH far faster on large datasets.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. Why does adding a vector index make a SQL Server table read-only?<\/h3>\n            <div class=\"faq-answer\">\n                <p>It&#8217;s a current limitation of the diskann index type in SQL Server 2025. Microsoft is aware of it \u2014 the restriction has already been lifted in updated Azure SQL Database regions.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">5. What changed in the Azure SQL Database vector search syntax?<\/h3>\n            <div class=\"faq-answer\">\n                <p>The new syntax moves TOP out of VECTOR_SEARCH and into the outer query with the WITH APPROXIMATE keyword, improving filtering performance and result consistency.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>Learn how to use VECTOR_DISTANCE and VECTOR_SEARCH in SQL Server to find semantically similar data. Covers vector indexes, performance trade-offs, and the latest Azure SQL Database syntax changes.&hellip;<\/p>\n","protected":false},"author":346483,"featured_media":106044,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527,143523,53,143524],"tags":[4168,4170,159401,4150,4151],"coauthors":[159368],"class_list":["post-111121","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-database-administration-sql-server","category-databases","category-featured","category-sql-server","tag-database","tag-database-administration","tag-greglowollamaseries","tag-sql","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111121","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/346483"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=111121"}],"version-history":[{"count":12,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111121\/revisions"}],"predecessor-version":[{"id":111422,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111121\/revisions\/111422"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/106044"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=111121"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=111121"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=111121"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=111121"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}