<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Rolfe</title>
    <description>The latest articles on DEV Community by David Rolfe (@srmadscience).</description>
    <link>https://dev.to/srmadscience</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4074374%2F27185e01-d9b2-4f91-8887-13bfa5f0a192.jpg</url>
      <title>DEV Community: David Rolfe</title>
      <link>https://dev.to/srmadscience</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/srmadscience"/>
    <language>en</language>
    <item>
      <title>Can You Run Hybrid Search on One Database? Yes! Here's How CrateDB Does It</title>
      <dc:creator>David Rolfe</dc:creator>
      <pubDate>Wed, 12 Aug 2026 09:45:15 +0000</pubDate>
      <link>https://dev.to/crate/can-you-run-hybrid-search-on-one-database-yes-heres-how-cratedb-does-it-42i</link>
      <guid>https://dev.to/crate/can-you-run-hybrid-search-on-one-database-yes-heres-how-cratedb-does-it-42i</guid>
      <description>&lt;h2&gt;
  
  
  Search has got more powerful, but also more complicated
&lt;/h2&gt;

&lt;p&gt;It used to be that database queries were simple enough. Either you had an index, or you didn't, and either way you had some kind of &lt;a href="https://en.wikipedia.org/wiki/Query_optimization" rel="noopener noreferrer"&gt;optimizer&lt;/a&gt; (cost or rule) that turned your SQL statement into a viable plan for finding and returning your data. &lt;/p&gt;

&lt;p&gt;We now live in a world where in addition to the Boolean logic of traditional RDBMS queries, we also have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Geospatial queries&lt;/li&gt;
&lt;li&gt;Full text search queries, using &lt;a href="https://en.wikipedia.org/wiki/Okapi_BM25" rel="noopener noreferrer"&gt;BM25&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Vector Search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if that weren't enough, we have to consider that instead of a traditional application issuing the query, it might be an &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; server, and most importantly of all, the business need might be for two or more of these searches to happen at the same time, on the same data. For example: &lt;/p&gt;

&lt;p&gt;"An MCP server that uses a single, combined geospatial + full text query to identify towns in Bavaria with castles mentioned in text descriptions."&lt;/p&gt;

&lt;h2&gt;
  
  
  How does CrateDB help with this?
&lt;/h2&gt;

&lt;p&gt;CrateDB is one of the limited number of products that not only supports all of these search types but is also capable of storing arbitrarily large quantities of data. This is important, as if you have to hit multiple different database servers to solve your business question, not only is your environment much more complicated, but you risk getting incorrect answers as your multiple databases may be out of sync. &lt;/p&gt;

&lt;p&gt;In this two-part example, based on our playable IOT Analytics scenario, we will show two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using Geo + Text to search a weather/tourism database&lt;/li&gt;
&lt;li&gt;Using CrateDB as a '360 view' for your MCP server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to follow along with this post, the setup is based on our &lt;a href="https://dev.to/explore/iot-analytics"&gt;IoT Analytics&lt;/a&gt; scenario. You can also just skip this blog post and jump straight to the scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Geo + Full text to search a weather/tourism database
&lt;/h2&gt;

&lt;p&gt;The table we're going to use is called '&lt;a href="https://github.com/crate/cratedb-explore/blob/main/sda/sql/german_weather_data_ddl.sql#L37C1-L45C3" rel="noopener noreferrer"&gt;German Regions&lt;/a&gt;':&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;german_regions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;region_name&lt;/span&gt;        &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;geo_coords&lt;/span&gt;         &lt;span class="n"&gt;GEO_SHAPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tourism_info&lt;/span&gt;       &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;FULLTEXT&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;transportation&lt;/span&gt;     &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;FULLTEXT&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;economics&lt;/span&gt;          &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;FULLTEXT&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;introduced_species&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;FULLTEXT&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt;          &lt;span class="n"&gt;FLOAT_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We load this table from a &lt;a href="https://github.com/crate/cratedb-explore/blob/main/sda/sql/german_weather_data_dml.sql#L3726-L3729" rel="noopener noreferrer"&gt;SQL file&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;german_regions&lt;/span&gt;
    &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;02632141&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;02049255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;01354980&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00099564&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Baden-Württemberg'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The embedding was calculated using the &lt;a href="https://developers.openai.com/api/docs/models/text-embedding-3-small" rel="noopener noreferrer"&gt;text-embedding-3-small&lt;/a&gt; model on all 4 columns concatenated together.&lt;/p&gt;

&lt;p&gt;The 'region_name' stores the &lt;a href="https://simple.wikipedia.org/wiki/States_of_Germany" rel="noopener noreferrer"&gt;'Bundesländer'&lt;/a&gt;, and the matching geo_coords store its shape. For demo purposes, we've simplified the shape slightly. All the SQL below can be found on &lt;a href="https://github.com/crate/cratedb-explore/blob/main/blogs/HybridColumns/examples.sql" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So as an example, if I want to find out which Bundesland Stuttgart is in, I can issue the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"demo"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"german_regions"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;WITHIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POINT( 9.0120664 48.7793174)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;geo_coords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Statement Results&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="n"&gt;region_name&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Baden&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="err"&gt;ü&lt;/span&gt;&lt;span class="n"&gt;rttemberg&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------------------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full text queries work in a similar way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"demo"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"german_regions"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tourism_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'castles'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;_score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Statement Results&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-------------------+------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="n"&gt;region_name&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="n"&gt;_score&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-------------------+------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;61765057&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Baden&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="err"&gt;ü&lt;/span&gt;&lt;span class="n"&gt;rttemberg&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;61096525&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Bayern&lt;/span&gt;            &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;539846&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-------------------+------------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with CrateDB we can also combine them! Suppose I'm visiting Stuttgart (9E, 48N) and want to find the 10 closest towns in the top region for wine production.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;matched_region&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;geo_coords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;german_regions&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tourism_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transportation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;introduced_species&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'wine vineyards'&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;_score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                                &lt;span class="c1"&gt;-- top BM25 hit only&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nearest_town&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geo_location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'POINT(9.0120664 48.7793174)'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;LONG&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;distance_m&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;matched_region&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geo_points&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;WITHIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geo_location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geo_coords&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;distance_m&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Statement Results&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-----------------+-----------+-----------------------+-------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="n"&gt;region_name&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;_score&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;nearest_town&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;distance_km&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-----------------+-----------+-----------------------+-------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="n"&gt;Hagenbach&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;60&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="n"&gt;Lustadt&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;76&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="n"&gt;Dernbach&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;90&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;Weisenheim&lt;/span&gt; &lt;span class="n"&gt;am&lt;/span&gt; &lt;span class="n"&gt;Sand&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;97&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="n"&gt;Merzalben&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;105&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="n"&gt;Ramsen&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;108&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;Dittelsheim&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;He&lt;/span&gt;&lt;span class="err"&gt;ß&lt;/span&gt;&lt;span class="n"&gt;loch&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;121&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="n"&gt;Otterberg&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;121&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rieschweiler&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="err"&gt;ü&lt;/span&gt;&lt;span class="n"&gt;hlbach&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;122&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rheinland&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Pfalz&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4929237&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;Nack&lt;/span&gt;          &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="mi"&gt;130&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-----------------+-----------+-----------------------+-------------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Vector searches
&lt;/h2&gt;

&lt;p&gt;Doing a KNN search is slightly more complicated. Our query term needs to be encoded as a vector. While CrateDB can do the vector search, it can't do the encoding itself. So we pass our search term to OpenAI, which returns a vector. This assumes you have a working OpenAI API key. Examples of how to do this are available in &lt;a href="https://github.com/crate/cratedb-explore/blob/6804ef49cd33b263fb4752de0ead0eb6d4dd7154/sda/src/src_knn_search/main/java/CrateDbKnnSearch.java#L285-L304" rel="noopener noreferrer"&gt;Java&lt;/a&gt;, &lt;a href="https://github.com/crate/cratedb-explore/blob/main/sda/src/src_knn_search/main/dotnet/Program.cs#L259C1-L275C2" rel="noopener noreferrer"&gt;.NET&lt;/a&gt;, and &lt;a href="https://github.com/crate/cratedb-explore/blob/main/sda/src/src_knn_search/main/python/cratedb_knn_search.py#L183-L198" rel="noopener noreferrer"&gt;Python&lt;/a&gt;. The actual Python code is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;knn_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[info] embedding query: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_column&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, _score &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FROM   &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TABLE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WHERE  KNN_MATCH(embedding, %s, %s) &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORDER  BY _score DESC &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LIMIT  %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;_print_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that being a KNN search you'll always get something back. Once you have your embedding, there is nothing stopping you from combining KNN with Geo or Full Text searches. Given that creating embeddings costs money, you might want to consider caching embeddings if doing this at scale or in production. Our Real-Time Industrial Analytics scenario includes &lt;a href="https://github.com/crate/cratedb-explore/blob/703e4c235e953b710413f1f324cf01d68b227b4f/rtia/src/src_rag/rtia_rag.py#L275-L297" rel="noopener noreferrer"&gt;example code&lt;/a&gt; showing how to accomplish that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using CrateDB as a '360 view' for your LLM, via an MCP server
&lt;/h2&gt;

&lt;p&gt;This next section of the log uses code from the &lt;a href="https://github.com/crate/cratedb-explore/tree/main/sda/src/src_mcp_search_german_weather" rel="noopener noreferrer"&gt;Sensor Data Analytics (SDA) scenario&lt;/a&gt;, in case you want to follow along. &lt;/p&gt;

&lt;p&gt;In this scenario, we went for a very simple MCP server – we allowed it to access the tables and gave it some &lt;a href="https://github.com/crate/cratedb-explore/blob/main/sda/src/src_mcp_search_german_weather/german_weather_mcp.py#L119-L146" rel="noopener noreferrer"&gt;general advice about how to issue queries&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Tools query a CrateDB cluster of German weather and regional data in the &lt;br&gt;
&lt;code&gt;demo\&lt;/code&gt; schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;climate_data (geo_location geo_point, measurement_time,&lt;/li&gt;
&lt;li&gt;data['temperature'] in Kelvin),&lt;/li&gt;
&lt;li&gt;german_regions (16 Laender with geo_coords  polygons plus full-text columns economics, transportation and  introduced_species - use MATCH() on these to answer questions about a  region's industry (e.g. car factories), transport or wildlife),&lt;/li&gt;
&lt;li&gt;geo_points  (station locations).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool instructions explain how to use it:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MANDATORY FIRST STEP: never run a data query without first confirming the&lt;/em&gt; &lt;br&gt;
&lt;em&gt;actual table and column names. Before any SELECT against the data, query&lt;/em&gt; &lt;br&gt;
&lt;em&gt;information_schema (e.g. SELECT table_name FROM information_schema.tables&lt;/em&gt; &lt;br&gt;
&lt;em&gt;WHERE table_schema = 'demo', then SELECT column_name, data_type FROM&lt;/em&gt; &lt;br&gt;
&lt;em&gt;information_schema.columns WHERE table_schema = 'demo' AND table_name =&lt;/em&gt; &lt;br&gt;
&lt;em&gt;'&amp;lt;table&amp;gt;') and write your query using only the table and column names that&lt;/em&gt; &lt;br&gt;
&lt;em&gt;those results return. The schema summary above is guidance, not a&lt;/em&gt; &lt;br&gt;
&lt;em&gt;substitute for this check.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Temperatures are Kelvin - always show Celsius first, Kelvin in&lt;/em&gt; &lt;br&gt;
&lt;em&gt;parentheses, e.g. -8.99 C (264.16 K).&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;For ANY 'where in Germany' / most-extreme-place question you MUST&lt;/em&gt; &lt;br&gt;
&lt;em&gt;restrict candidates with WITHIN(c.geo_location, r.geo_coords) by joining&lt;/em&gt; &lt;br&gt;
&lt;em&gt;climate_data c to german_regions r; geo_points alone leaks near-border foreign towns (e.g. Tannheim in Tyrol, Austria).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;When a query touches geo_points, and the user gives no time range, limit it to the latest data with measurement_time = (SELECT MAX(d2.measurement_time) FROM demo.climate_data d2)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;End every SQL statement with LIMIT 1000 unless the user instructs you otherwise.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Installation also involves a .mcp.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"mcpServers"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"german-weather"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"command"&lt;/span&gt;: &lt;span class="s2"&gt;"/Users/dwrolfe/IdeaProjects/foo/sda/src/src_mcp_search_german_weather/.venv/bin/python"&lt;/span&gt;,
      &lt;span class="s2"&gt;"args"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/Users/dwrolfe/IdeaProjects/foo/sda/src/src_mcp_search_german_weather/german_weather_mcp.py"&lt;/span&gt;,
               &lt;span class="s2"&gt;"--cratedb-url"&lt;/span&gt;,
               &lt;span class="s2"&gt;"http://XXXXXX:4200/"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
      &lt;span class="s2"&gt;"env"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"CRATEDB_USER"&lt;/span&gt;: &lt;span class="s2"&gt;"XXXXX"&lt;/span&gt;,
        &lt;span class="s2"&gt;"CRATEDB_PASSWORD"&lt;/span&gt;: &lt;span class="s2"&gt;"tigXXXXXer"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run it, we restart Claude and check that the MCP server is connected, using the /mcp command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;/mcp&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;German-weather MCP Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status: ✔ connected&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we can ask questions:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❯ What questions can I ask the german-weather MCP server?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The german-weather server exposes a single query_sql tool over the demo schema, so you can ask anything answerable with SQL against these three tables:&lt;br&gt;
Weather/climate questions (climate_data: temperatures in Kelvin, geo-located, timestamped)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What's the coldest place in Germany right now?"&lt;/li&gt;
&lt;li&gt;"What was the hottest temperature recorded last week, and where?"&lt;/li&gt;
&lt;li&gt;"Show me the average temperature near Munich over the past month."&lt;/li&gt;
&lt;li&gt;"How has the temperature at station X changed over time?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regional / geographic questions (german_regions: the 16 Bundesländer with polygon boundaries and full-text-indexed economics, transportation, and introduced_species columns)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Which German regions have car factories?" (Full-text search MATCH on economics)&lt;/li&gt;
&lt;li&gt;"Which Länder mention raccoons as an introduced species?"&lt;/li&gt;
&lt;li&gt;"What transportation infrastructure does Bavaria have?"&lt;/li&gt;
&lt;li&gt;"Which region contains this coordinate?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Station questions (geo_points: 726 weather-station locations with nearest_town)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What's the nearest weather station to Hamburg?"&lt;/li&gt;
&lt;li&gt;"How many stations are there per region?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combined questions are where it gets interesting — e.g. "Which Bundesland was coldest yesterday?" or "What's the temperature at the station nearest Berlin?" — joining climate data to regions or stations.&lt;/p&gt;

&lt;p&gt;Two rules the server bakes into its instructions (so the answering model applies them automatically):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"In Germany" questions are polygon-filtered using WITHIN(...) against german_regions because geo_points includes a few near-border foreign towns, such as Tannheim in Tyrol, Austria.&lt;/li&gt;
&lt;li&gt;Temperatures are stored in Kelvin and always displayed Celsius-first, e.g., -8.99 C (264.16 K).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since it's just SQL underneath, arbitrary aggregations, top-N lists, and time-range comparisons all work too. Want me to try one of these against the server now?&lt;/p&gt;

&lt;p&gt;✻ Cooked for 14s&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❯ Which place has the biggest temperature range?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A "biggest range" question — that's a most-extreme-place query, so I'll polygon-filter to Germany per the server rules. First, confirming the columns of the two tables involved.&lt;/p&gt;

&lt;p&gt;Called german-weather 6 times&lt;/p&gt;

&lt;p&gt;Vierlinden in Brandenburg (52.5°N, 14.25°E, near the Polish border east of Berlin) has the biggest temperature range in the dataset: a span of 36.70 °C, from a low of -8.98 C (264.17 K) to a high of 27.73 C (300.88 K).&lt;/p&gt;

&lt;p&gt;The Germany polygon check confirmed the point falls inside Brandenburg, which mattered here — it's right by the border, and the runner-up locations are all in the same continental Berlin/Brandenburg area (spans of 36.6, 36.5, and 36.4 °C at points in and around Berlin). That's the expected pattern: eastern Germany's continental climate swings far more between winter and summer than the maritime northwest.&lt;/p&gt;

&lt;p&gt;One note on method: the single-query version with the polygon join across all readings timed out, so I aggregated ranges per grid point first and then polygon-checked only the leaders — same result, just computed in two steps.&lt;/p&gt;

&lt;p&gt;✻ Churned for 2m 17s&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❯ How far is that from the place with the smallest temperature range?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I'll find the smallest-range location the same way — aggregate first, then Germany-check the candidates (the smallest ranges will be maritime, so offshore/foreign points are a real risk).&lt;/p&gt;

&lt;p&gt;Called german-weather&lt;/p&gt;

&lt;p&gt;All the smallest-range points are on the North Sea/Baltic coast, as expected. Now checking which of the leading candidates actually fall inside a German Land polygon (estuary/offshore grid points may not).&lt;/p&gt;

&lt;p&gt;Called german-weather 3 times&lt;/p&gt;

&lt;p&gt;They're 392 km apart (392.3 km great-circle).&lt;/p&gt;

&lt;p&gt;The smallest-range place in Germany is Wanna in Niedersachsen (53.75°N, 8.75°E, near the Elbe/Weser estuaries by Cuxhaven), with a span of just 24.86 °C — from -2.10 C (271.05 K) to 22.76 C (295.91 K). Compare Vierlinden's 36.70 °C span: the maritime coast swings about 12 °C less than the continental east.&lt;/p&gt;

&lt;p&gt;One catch worth noting: the grid point with the truly smallest range (53.75°N, 8.5°E, 23.57 °C span) failed the WITHIN check — it sits offshore in the Wadden Sea, as did two other leading candidates. Wanna is the smallest-range point that's actually on German land, which is exactly the near-border leak the server's polygon rule exists to catch.&lt;/p&gt;

&lt;p&gt;So the dataset's two climate extremes — most continental and most maritime — sit at opposite ends of a 392 km line running roughly from the North Sea coast southeast to the Polish border.&lt;/p&gt;

&lt;p&gt;What's apparent from this interaction is that the performance of an MCP server connected to CrateDB (or any database...) is going to be heavily influenced by the prompts and instructions you give it. While it does give users considerable flexibility, it's not foolproof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog post, we've shown you how CrateDB not only supports newer data formats such as full-text but also serves as a "360 view" of your business. If you want to find out more, we'd recommend you &lt;a href="https://dev.to/explore/iot-analytics?use-case=iot"&gt;run this full scenario&lt;/a&gt; yourself.&lt;/p&gt;

</description>
      <category>database</category>
      <category>search</category>
      <category>sql</category>
    </item>
    <item>
      <title>Zero Downtime Schema Changes With CrateDB</title>
      <dc:creator>David Rolfe</dc:creator>
      <pubDate>Wed, 12 Aug 2026 08:32:10 +0000</pubDate>
      <link>https://dev.to/crate/zero-downtime-schema-changes-with-cratedb-3f36</link>
      <guid>https://dev.to/crate/zero-downtime-schema-changes-with-cratedb-3f36</guid>
      <description>&lt;h2&gt;
  
  
  CrateDB's Dynamic Objects Avoid Outages, Downtime, and Human Intervention, Saving Time and Money
&lt;/h2&gt;

&lt;p&gt;Very few large production systems exist without change. In every real-world database system the author has seen, implementing and managing change is not just a 'fact of life', it's the main activity. You'd never know this from reading Medium posts, or articles on LinkedIn, which generally focus on the 'new and shiny', but once a system is up and running, and has upstream and downstream customers, an apparently simple change can start a chain reaction of work that can tie up an entire team for days. Google's stats show that 70% of outages result from a change. Bear in mind it's not enough to devise a 'method of procedure', a cookbook for how to do the change. You also must test it, which implies you have a test environment with up-to-date data, and it needs to be large enough to reveal any issues.&lt;/p&gt;

&lt;p&gt;One of the more interesting design features of CrateDB is how it can act like a document database when it wants to. Specifically, it's possible to set up a CrateDB table so that if it's asked to insert a column it's never seen before, it adds it at runtime.&lt;/p&gt;

&lt;p&gt;Let's walk through an example of this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;my_usecase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_network_devices&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;device_id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;reading_timestamp&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;mac&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;reported_location&lt;/span&gt; &lt;span class="k"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;STRICT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt; &lt;span class="nb"&gt;DOUBLE&lt;/span&gt; &lt;span class="nb"&gt;PRECISION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;long&lt;/span&gt; &lt;span class="nb"&gt;DOUBLE&lt;/span&gt; &lt;span class="nb"&gt;PRECISION&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;stuff_we_search&lt;/span&gt; &lt;span class="k"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DYNAMIC&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;stuff_we_dont_search&lt;/span&gt; &lt;span class="k"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IGNORED&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;reading_timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the table above, we define three 'objects', each of which has one of three policies supported by CrateDB:&lt;/p&gt;

&lt;p&gt;STRICT means that the object has a fixed number of attributes with fixed types. All possible attribute keys must be pre-declared. Unknown keys are rejected. All values are indexed. In this case, we are storing latitude and longitude.&lt;/p&gt;

&lt;p&gt;DYNAMIC is the default, and where things get interesting. New inner keys are accepted, and each one is added to the schema and indexed on first sight. The first value seen is used to infer a data type, and subsequent values will be cast to that data type. All values are indexed.&lt;/p&gt;

&lt;p&gt;IGNORED doesn't actually mean we 'ignore' the data. It means we have no idea what kind of data we're going to get. We won't make assumptions about data types. We store everything we get, but don't enforce a schema and don't index the values.&lt;/p&gt;

&lt;p&gt;What does this mean in practice? As a developer, I generally don't need to know every possible low-level data item for every possible device we might see. For people in the IoT IoT space, this is a blessing! A lot of devices just love producing streams of stats and data points that are obscure and may appear or disappear every time there is a firmware update. Here's a sample of the kind of data we're talking about, 'radio stats' for a router:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkj4x8vh5x3dsc6xs58gl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkj4x8vh5x3dsc6xs58gl.png" alt="Screenshot of a JSON-like device data payload (e.g., from a router API) showing fields such as hardware version, MAC addresses, description (" width="403" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a DBA, I have no clue what half of this is. I just know we need to store it. I can say that if I load data from a different model of router, or a router with different firmware, I will get a slightly different set of stats. If we just need to store this in CrateDB, we can use OBJECT(IGNORED). If we need to index specific columns so we can query them efficiently in SQL, we can use OBJECT(DYNAMIC).&lt;/p&gt;

&lt;p&gt;In our GitHub repository, we have a small example of this that you can use in standalone CrateDB or CrateDB Cloud. Having created the table above, we insert a row, and then see what the table structure looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;my_usecase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_network_devices&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reading_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reported_location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stuff_we_search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stuff_we_dont_search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'38U10M57C03110'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="s1"&gt;'10.13.1.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'D8:EC:5E:8E:ED:9E'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1374&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;long&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5755&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Router'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Velop AX4200 WiFi 6 System'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;manufacturer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Linksys'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MX42-EU'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fw_ver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1.0.13.216903'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;hw_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'48SAQB11.0GA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;serial_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'38U10M57C03110'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;extra_macs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'de:ec:5e:8e:ed:9f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'d8:ec:5e:8e:ed:a1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'d8:ec:5e:8e:ed:a0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'da:ec:5e:8e:ed:a2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'e6:ec:5e:8e:ed:9f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'d8:ec:5e:8e:ed:9e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'e2:ec:5e:8e:ed:9f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'d8:ec:5e:8e:ed:9f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'de:ec:5e:8e:ed:a0'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nv"&gt;"userAp2G_bssid"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'D8:EC:5E:8E:ED:9F'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"userAp2G_channel"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'13'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait! Haven't we changed the schema by inserting into it? Yes, we have:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5zc9imzqp6bwseiu0jl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5zc9imzqp6bwseiu0jl.png" alt="Screenshot of a SQL SHOW CREATE TABLE output for a CrateDB table named my_usecase.my_network_devices, showing column definitions including device_id, reading_timestamp, ip, mac, a reported_location OBJECT(STRICT) with lat/long fields, a stuff_we_search OBJECT(DYNAMIC) with several text fields, a stuff_we_dont_search OBJECT(IGNORED) column, a composite primary key, and table clustering/replica settings." width="513" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that the schema change is only reported for the DYNAMIC column. The IGNORED column still has data, but it doesn't show up in the schema. It's searchable, but you may need to cast search terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So what does this mean, and why does it matter? New columns show up all the time in live systems. In a traditional RDBMS, this means an ALTER TABLE statement, which sets off a whole chain of tasks and may lead to either downtime or a scenario where the backup system has a different schema to the live system, which is problematic. In CrateDB, there is no need for human intervention at all.&lt;/p&gt;

&lt;p&gt;Do you want to try it on your own use case?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cratedb.com/start-free" rel="noopener noreferrer"&gt;Get Started for Free with CrateDB&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>iot</category>
    </item>
  </channel>
</rss>
