<?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: VeilAnalytics</title>
    <description>The latest articles on DEV Community by VeilAnalytics (@veilanalytics).</description>
    <link>https://dev.to/veilanalytics</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%2F4106313%2F56280f96-591c-4780-8543-fc3c3d10ea6f.png</url>
      <title>DEV Community: VeilAnalytics</title>
      <link>https://dev.to/veilanalytics</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veilanalytics"/>
    <language>en</language>
    <item>
      <title>How to Build and Customize Interactive Dashboards from CSV Files (Without Cloud BI Tools)</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Mon, 21 Sep 2026 15:42:01 +0000</pubDate>
      <link>https://dev.to/veilanalytics/how-to-build-and-customize-interactive-dashboards-from-csv-files-without-cloud-bi-tools-168</link>
      <guid>https://dev.to/veilanalytics/how-to-build-and-customize-interactive-dashboards-from-csv-files-without-cloud-bi-tools-168</guid>
      <description>&lt;h2&gt;
  
  
  How to Build and Customize Interactive Dashboards from CSV Files (Without Cloud BI Tools)
&lt;/h2&gt;

&lt;p&gt;Every business analyst, researcher, and product manager faces the same recurring chore:&lt;/p&gt;

&lt;p&gt;You have raw data in a CSV or Excel export, and leadership wants a clean, interactive dashboard for a weekly review or client presentation.&lt;/p&gt;

&lt;p&gt;The standard options come with serious trade-offs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tableau / PowerBI / Looker:&lt;/strong&gt; Expensive enterprise subscriptions, steep learning curves, and mandatory cloud uploads that trigger corporate security reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spreadsheet Charts (Excel / Google Sheets):&lt;/strong&gt; Clunky, static, difficult to customize, and freeze when handling more than a couple hundred thousand records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing Custom Frontend Code:&lt;/strong&gt; Building React/Vue charts with Chart.js or D3 from scratch takes hours of frontend development for what should be a quick report.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a modern alternative: &lt;strong&gt;building customizable, interactive dashboards directly in your browser with zero data uploads&lt;/strong&gt;, and exporting them as portable offline HTML reports.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 What Makes a Great Ad-Hoc Analytics Dashboard?
&lt;/h2&gt;

&lt;p&gt;When presenting business or operational data, stakeholders care about three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High-Level KPIs:&lt;/strong&gt; Total revenue, average order value, churn rate, incident count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trend Visualizations:&lt;/strong&gt; Time-series charts showing performance changes week-over-week.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Breakdown:&lt;/strong&gt; Ability to filter by category, region, or status on the fly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To build this quickly from a raw dataset without sending files across the internet, you need an engine that can aggregate multi-gigabyte files client-side and dynamically map results to interactive visualization components.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Step 1: Rapid Local Aggregation
&lt;/h2&gt;

&lt;p&gt;Instead of loading millions of raw rows into memory, modern in-browser SQL engines aggregate the data first:&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="c1"&gt;-- Generate monthly revenue by product category&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%Y-%m'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&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;total_revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;order_id&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;total_orders&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'sales_2024.csv'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&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;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this query runs in-memory via DuckDB, the aggregation completes in milliseconds, providing structured arrays ready for rendering.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Step 2: Customizing Visualizations for Your Audience
&lt;/h2&gt;

&lt;p&gt;Different questions demand different visual representations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Analytical Question&lt;/th&gt;
&lt;th&gt;Recommended Visualization&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;"How is revenue trending month-over-month?"&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Line or Area Chart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;"What is the market share per product category?"&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Donut or Bar Chart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;"Where are our highest-value accounts located?"&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Ranked Horizontal Bar Chart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;"What are our key snapshot numbers?"&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Stat Cards with % Delta&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  📁 Step 3: Standalone Offline HTML Export
&lt;/h2&gt;

&lt;p&gt;One of the biggest friction points with cloud BI platforms is sharing: viewers must create accounts, log into organization workspaces, or have active license seats.&lt;/p&gt;

&lt;p&gt;A superior workflow is generating a &lt;strong&gt;self-contained HTML dashboard report&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single File:&lt;/strong&gt; Includes the HTML, CSS, interactive Chart.js bundle, and aggregated data tables in one file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Server Dependency:&lt;/strong&gt; Double-click the file and it opens instantly in any web browser (Chrome, Safari, Firefox, Edge).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100% Offline Capability:&lt;/strong&gt; Operates completely without internet access — ideal for air-gapped enterprise environments or in-flight presentations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Licensing Friction:&lt;/strong&gt; Anyone on your team can view, interact with, and inspect the visualizations for free.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 How VeilAnalytics Simplifies the Dashboard Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; is designed specifically around this fast, privacy-first dashboard creation loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Instant In-Browser Ingestion:&lt;/strong&gt; Drag and drop your CSV, JSON, or Parquet file into &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;veilanalytics.netlify.app&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversational Dashboard Building:&lt;/strong&gt; Simply describe what you want to visualize:

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"Build a dashboard showing revenue trends by region and top 10 products"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Create KPI summary cards for total users, active accounts, and conversion rate"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize Your Metrics:&lt;/strong&gt; Adjust chart types, date ranges, and grouping dimensions instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1-Click Portable Export:&lt;/strong&gt; Export the entire dashboard as a standalone offline HTML document to email to colleagues or attach to reports.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your raw source data never leaves your browser tab, ensuring full compliance with corporate security and privacy policies.&lt;/p&gt;




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

&lt;p&gt;You don't need heavyweight cloud BI stacks or complex coding frameworks to generate polished, interactive reports. By leveraging in-browser computation and automated dashboard generation, you can turn raw CSVs and JSON files into presentation-ready dashboards in minutes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — Build interactive dashboards from your CSV, JSON, and Parquet data. Runs 100% in your browser with zero data uploads.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>database</category>
      <category>datascience</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Query Server and Application Logs with SQL (No ELK Stack or Cloud Uploads)</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Sun, 20 Sep 2026 13:28:50 +0000</pubDate>
      <link>https://dev.to/veilanalytics/how-to-query-server-and-application-logs-with-sql-no-elk-stack-or-cloud-uploads-4blc</link>
      <guid>https://dev.to/veilanalytics/how-to-query-server-and-application-logs-with-sql-no-elk-stack-or-cloud-uploads-4blc</guid>
      <description>&lt;h2&gt;
  
  
  How to Query Server and Application Logs with SQL (No ELK Stack or Cloud Uploads)
&lt;/h2&gt;

&lt;p&gt;Every developer and DevOps engineer knows the drill during an incident:&lt;/p&gt;

&lt;p&gt;The server is throwing errors, and you need to answer critical questions immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"Which IP addresses are hitting &lt;code&gt;/api/auth&lt;/code&gt; more than 50 times a minute?"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"What are the top 10 slow endpoints causing 504 Gateway Timeouts?"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Show me the distribution of status codes between 02:00 and 03:00 UTC."&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The traditional solutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Piping &lt;code&gt;grep | awk | cut | sort | uniq -c | sort -nr&lt;/code&gt;&lt;/strong&gt;: One typo in your awk column index or regex, and you lose 15 minutes of triage time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heavy Logging Stacks (ELK / Datadog / CloudWatch)&lt;/strong&gt;: Expensive to ingest, complex to maintain for ad-hoc post-mortems, or simply unavailable on standalone servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uploading raw log files to online log parsers / AI&lt;/strong&gt;: A critical security violation. Server logs contain real customer IP addresses, authorization tokens, user-agent fingerprints, and internal infrastructure URLs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is how to query raw access logs and error logs directly using &lt;strong&gt;standard SQL and natural language&lt;/strong&gt;, running 100% locally.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Step 1: Parsing Access Logs into SQL Columns
&lt;/h2&gt;

&lt;p&gt;Whether your logs are in standard Common Log Format (CLF), Nginx combined format, or JSON, you can extract structured columns without prior database import.&lt;/p&gt;

&lt;p&gt;For example, using DuckDB's regex extraction on an Nginx access log:&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;regexp_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'^(&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="s1"&gt;+)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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;client_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;regexp_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;(GET|POST|PUT|DELETE)&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;+([^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;]+)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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;request_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;regexp_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"\s&lt;/span&gt;&lt;span class="s1"&gt;+(&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;{3})&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;+'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;regexp_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;+(&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+)&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;response_size&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'access.log'&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;status_code&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;4&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;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡ Step 2: Querying Structured JSON Logs Directly
&lt;/h2&gt;

&lt;p&gt;Modern microservices emit structured logs (e.g., Winston, Zap, Pino, Logstash) as NDJSON lines. This is where SQL shines brightest:&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="c1"&gt;-- Identify the top failing services and error messages&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&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;incident_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;timestamp&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;first_seen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;timestamp&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;last_seen&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;read_ndjson_auto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'production_errors.log'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ERROR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'FATAL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&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;2&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;incident_count&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With one readable query, you get aggregated statistics, counts, and time boundaries that would have required 30 lines of shell commands.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 Security &amp;amp; Privacy: Why Logs Must Stay Local
&lt;/h2&gt;

&lt;p&gt;Server logs contain sensitive information protected under GDPR, HIPAA, and CCPA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client IP addresses (classified as PII)&lt;/li&gt;
&lt;li&gt;Query parameters that may inadvertently include session tokens or email addresses&lt;/li&gt;
&lt;li&gt;Internal hostnames and API endpoint topology&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running queries in a local, isolated environment guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero data exfiltration risk:&lt;/strong&gt; Raw log files are read from local storage into memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit compliance:&lt;/strong&gt; You do not trigger third-party data processor agreements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immediate availability:&lt;/strong&gt; Works offline, even during catastrophic cloud outages.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Natural Language Log Analysis with VeilAnalytics
&lt;/h2&gt;

&lt;p&gt;If you want to investigate logs without writing complex SQL or regex strings manually:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; provides an in-browser workspace designed for zero-raw-data analytics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local File Ingestion:&lt;/strong&gt; Load your &lt;code&gt;.log&lt;/code&gt;, &lt;code&gt;.jsonl&lt;/code&gt;, or &lt;code&gt;.csv&lt;/code&gt; log dumps directly into the browser tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversational Analysis:&lt;/strong&gt; Type questions like:

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"Show the top 5 endpoints generating 4xx errors today"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"What is the hourly distribution of HTTP 500 responses?"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Which IPs made the most requests during the spike?"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Browser Compute:&lt;/strong&gt; VeilAnalytics passes only table schema/column definitions to the LLM to generate the query, which runs client-side via DuckDB-WASM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Dashboard:&lt;/strong&gt; Generates interactive timeline charts and breakdown visualizations that you can export as a self-contained offline HTML report.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;Stop struggling with complex shell pipes or risking sensitive logs on cloud pastebins. Modern in-process SQL engines give you the full analytical power of a data warehouse right on your local machine.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — Safe, private, in-browser data analytics. Query logs, CSVs, and JSON files without sending raw data to the cloud.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>database</category>
      <category>privacy</category>
    </item>
    <item>
      <title>How to Query Complex JSON and NDJSON Files with SQL (Without Writing Custom Parsers)</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Sat, 19 Sep 2026 12:54:11 +0000</pubDate>
      <link>https://dev.to/veilanalytics/how-to-query-complex-json-and-ndjson-files-with-sql-without-writing-custom-parsers-409m</link>
      <guid>https://dev.to/veilanalytics/how-to-query-complex-json-and-ndjson-files-with-sql-without-writing-custom-parsers-409m</guid>
      <description>&lt;h2&gt;
  
  
  How to Query Complex JSON and NDJSON Files with SQL (Without Writing Custom Parsers)
&lt;/h2&gt;

&lt;p&gt;Nested JSON is the universal language of APIs, event streams, and database dumps.&lt;/p&gt;

&lt;p&gt;But when you need to answer a quick analytical question like &lt;em&gt;"What was the average response time for customer_id: 4892 across this 500MB JSON dump?"&lt;/em&gt;, the options are usually painful:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write a quick Python script:&lt;/strong&gt; Write boilerplate dictionary loops, parse dates, handle missing keys, and debug &lt;code&gt;KeyError&lt;/code&gt; exceptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Import into MongoDB / Postgres:&lt;/strong&gt; Spin up a local container or instance, configure schemas or JSONB columns, write queries, and tear it down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload to an online JSON viewer / AI tool:&lt;/strong&gt; Expose internal API payloads, customer IDs, and sensitive tokens to third-party servers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a much cleaner way: &lt;strong&gt;query nested JSON directly using SQL in-memory&lt;/strong&gt;, keeping your data 100% on your machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Querying JSON Directly with DuckDB SQL
&lt;/h2&gt;

&lt;p&gt;Modern columnar engines like DuckDB have native JSON readers that automatically detect schemas and flatten nested objects on the fly:&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="c1"&gt;-- Read a JSON file directly as a virtual table&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;user_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;transaction_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;timestamp&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;read_json_auto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_events.json'&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'completed'&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&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;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do not need to define tables, specify column data types, or import anything. The engine inspects the file, extracts the structure, and lets you run standard SQL aggregations immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  📂 Handling NDJSON / JSON Lines (Massive Event Streams)
&lt;/h2&gt;

&lt;p&gt;For server logs and event tracking, files are usually formatted as &lt;strong&gt;Newline Delimited JSON (NDJSON / &lt;code&gt;.jsonl&lt;/code&gt;)&lt;/strong&gt;, where each line is a separate JSON object.&lt;/p&gt;

&lt;p&gt;NDJSON can scale to gigabytes, making standard Python &lt;code&gt;json.load()&lt;/code&gt; crash with Out-Of-Memory (OOM) errors because it tries to parse the entire tree into memory at once.&lt;/p&gt;

&lt;p&gt;DuckDB streams NDJSON in vectorized chunks:&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="c1"&gt;-- Aggregate metrics across gigabytes of NDJSON event logs&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%Y-%m-%d %H:00'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;TIMESTAMP&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;hourly_bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&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;request_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration_ms&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;avg_latency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;PERCENTILE_CONT&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;95&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;WITHIN&lt;/span&gt; &lt;span class="k"&gt;GROUP&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;duration_ms&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;p95_latency&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;read_ndjson_auto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'server_events.jsonl'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&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;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔒 The Privacy Imperative: Keeping API Dumps Confidential
&lt;/h2&gt;

&lt;p&gt;JSON exports from production systems frequently contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PII (emails, IP addresses, names)&lt;/li&gt;
&lt;li&gt;Authentication tokens or internal session IDs&lt;/li&gt;
&lt;li&gt;Proprietary customer financial transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uploading these dumps to generic cloud AI services or online JSON formatters is a major security liability.&lt;/p&gt;

&lt;p&gt;By running the query engine in-process or in-browser (via WebAssembly), the file is read directly from your local filesystem into sandboxed memory. No network packets containing your data are ever sent across the web.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Interactive In-Browser Querying: VeilAnalytics
&lt;/h2&gt;

&lt;p&gt;If you prefer a clean interface without writing CLI commands or installing local database engines:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; provides an in-browser analytics workspace powered by DuckDB-WASM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drop your &lt;code&gt;.json&lt;/code&gt; or &lt;code&gt;.jsonl&lt;/code&gt; file directly into the browser tab.&lt;/li&gt;
&lt;li&gt;Ask questions in natural language: &lt;em&gt;"Find the top 10 users by total transaction value in completed orders"&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The platform maps your request to an optimized read-only SQL query executed entirely inside your browser sandbox.&lt;/li&gt;
&lt;li&gt;Export interactive charts and dashboards as self-contained offline HTML files.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📋 Summary: When to Use SQL on JSON
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Traditional Python&lt;/th&gt;
&lt;th&gt;In-Memory SQL Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quick aggregations on 1GB+ JSON&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;50+ lines of loop code + high RAM&lt;/td&gt;
&lt;td&gt;Single SQL query in &amp;lt; 2 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nested field extraction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record.get('user', {}).get('id')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;user.id&lt;/code&gt; dot notation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Privacy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local, but tedious&lt;/td&gt;
&lt;td&gt;100% Local &amp;amp; Instant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P95 / Percentiles Calculation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires &lt;code&gt;numpy&lt;/code&gt; / manual sort&lt;/td&gt;
&lt;td&gt;Native &lt;code&gt;PERCENTILE_CONT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Next time you receive a multi-hundred megabyte JSON export, skip the custom parsing scripts. Query it with SQL locally.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — Private in-browser data analytics workspace. Query CSV, JSON, and Parquet with natural language and SQL. Zero data uploads.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>database</category>
      <category>webdev</category>
      <category>python</category>
    </item>
    <item>
      <title>Best Open-Source Text-to-SQL Tools in 2025 (Ranked by Privacy &amp; Performance)</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Fri, 18 Sep 2026 13:25:22 +0000</pubDate>
      <link>https://dev.to/veilanalytics/best-open-source-text-to-sql-tools-in-2025-ranked-by-privacy-performance-5emg</link>
      <guid>https://dev.to/veilanalytics/best-open-source-text-to-sql-tools-in-2025-ranked-by-privacy-performance-5emg</guid>
      <description>&lt;h2&gt;
  
  
  Best Open-Source Text-to-SQL Tools in 2025 (Ranked by Privacy &amp;amp; Performance)
&lt;/h2&gt;

&lt;p&gt;Text-to-SQL is one of the most practical AI capabilities available today. Instead of writing &lt;code&gt;SELECT region, SUM(revenue) FROM sales GROUP BY region ORDER BY 2 DESC&lt;/code&gt;, you ask "What's total revenue by region?" and the tool writes it for you.&lt;/p&gt;

&lt;p&gt;The market has exploded. There are now dozens of tools offering natural language SQL, ranging from cloud APIs to fully local open-source stacks. This guide ranks them on what matters most for production use: &lt;strong&gt;privacy&lt;/strong&gt;, &lt;strong&gt;accuracy&lt;/strong&gt;, &lt;strong&gt;setup complexity&lt;/strong&gt;, and &lt;strong&gt;database support&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How We Evaluated Them
&lt;/h2&gt;

&lt;p&gt;Each tool was tested on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard NL2SQL benchmarks (Spider, BIRD)&lt;/li&gt;
&lt;li&gt;Complex multi-table joins with ambiguous phrasing&lt;/li&gt;
&lt;li&gt;Privacy posture (what data leaves your environment)&lt;/li&gt;
&lt;li&gt;Ease of integration for a working developer&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tier 1: Fully Local, Maximum Privacy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🥇 VeilAnalytics + DuckDB-WASM
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy: ★★★★★ | Accuracy: ★★★★☆ | Setup: ★★★★★&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; takes the unique approach of running everything in the browser — DuckDB-WASM for compute, and a schema-only prompt approach for the LLM. Your actual data rows never leave your device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs SQL analytics in-browser with natural language input&lt;/li&gt;
&lt;li&gt;Schema-only context sent to LLM (never actual data rows)&lt;/li&gt;
&lt;li&gt;Results computed locally by DuckDB-WASM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ideal for:&lt;/strong&gt; Business analysts and developers who need quick, private analysis of CSV/Parquet files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; In development, targeting open source release&lt;/p&gt;




&lt;h3&gt;
  
  
  🥈 Vanna.AI (Local Mode)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy: ★★★★☆ | Accuracy: ★★★★★ | Setup: ★★★☆☆&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vanna.ai" rel="noopener noreferrer"&gt;Vanna.AI&lt;/a&gt; is one of the most production-ready open-source Text-to-SQL frameworks. It uses a Retrieval-Augmented Generation (RAG) approach: store your DDL, documentation, and example queries in a local vector store, then use them as context for each query.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;vanna.ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Ollama&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;vanna.chromadb&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChromaDB_VectorStore&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyVanna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChromaDB_VectorStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ollama&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ChromaDB_VectorStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;Ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;vn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyVanna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect_to_duckdb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_database.db&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Train on your schema (done once)
&lt;/span&gt;&lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ddl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE TABLE orders (id INT, customer_id INT, amount FLOAT, date DATE)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Query in natural language
&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What were the top 5 customers by spending last quarter?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sql&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improves over time with RAG training on your own schema&lt;/li&gt;
&lt;li&gt;Supports local ChromaDB (no cloud vector store required)&lt;/li&gt;
&lt;li&gt;Works with any LLM backend (Ollama, OpenAI BYOK, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More setup than simpler tools&lt;/li&gt;
&lt;li&gt;RAG accuracy depends on training quality&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🥉 SQLCoder (Defog)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy: ★★★★★ | Accuracy: ★★★★★ | Setup: ★★☆☆☆&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/defog-ai/sqlcoder" rel="noopener noreferrer"&gt;SQLCoder&lt;/a&gt; by Defog is a fine-tuned model specifically for Text-to-SQL, trained on real SQL workloads. It significantly outperforms general-purpose models (GPT-4, Llama) on complex queries.&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="c"&gt;# Run via Ollama (7B parameter quantized model)&lt;/span&gt;
ollama pull sqlcoder:7b

&lt;span class="c"&gt;# Via Python&lt;/span&gt;
import ollama
response &lt;span class="o"&gt;=&lt;/span&gt; ollama.generate&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sqlcoder'&lt;/span&gt;,
    &lt;span class="nv"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;f&lt;span class="s2"&gt;"""### Task
Generate SQL to answer: "&lt;/span&gt;What are total sales by region &lt;span class="k"&gt;for &lt;/span&gt;Q3 2024?&lt;span class="s2"&gt;"

### Database Schema
{your_schema_here}

### Answer
SELECT"""&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best-in-class accuracy on complex multi-table queries&lt;/li&gt;
&lt;li&gt;Optimized specifically for SQL (not a generalist model)&lt;/li&gt;
&lt;li&gt;Fully local via Ollama&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires GPU for reasonable performance (CPU is slow)&lt;/li&gt;
&lt;li&gt;No built-in UI — just the model&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tier 2: Cloud-Assisted (Schema Only, Not Data)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Outlines + Constrained Decoding
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy: ★★★★☆ | Accuracy: ★★★★☆ | Setup: ★★☆☆☆&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/outlines-dev/outlines" rel="noopener noreferrer"&gt;Outlines&lt;/a&gt; enables structured generation — you can constrain any LLM to output valid SQL syntax only, preventing hallucinated table names and invalid syntax:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;outlines&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;outlines.models&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transformers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mistralai/Mistral-7B-Instruct-v0.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sql_generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;outlines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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="c1"&gt;# Only generates valid SQL
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sql_generator&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;Schema: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;SQL:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dramatically reduces syntax errors in generated SQL.&lt;/p&gt;




&lt;h3&gt;
  
  
  LlamaIndex NLSQLTableQueryEngine
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy: ★★★★☆ | Accuracy: ★★★★☆ | Setup: ★★★☆☆&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://llamaindex.ai" rel="noopener noreferrer"&gt;LlamaIndex&lt;/a&gt; provides a high-level abstraction for Text-to-SQL that works with any SQL database and any LLM:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLDatabase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core.query_engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NLSQLTableQueryEngine&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.llms.ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Ollama&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ollama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sql_db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_tables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NLSQLTableQueryEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql_database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sql_db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which customers placed the most orders?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; Easy integration with existing SQLAlchemy databases, supports many LLMs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tier 3: Cloud API (Data Leaves Your Environment)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  OpenAI GPT-4 with Function Calling
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy: ★★☆☆☆ | Accuracy: ★★★★★ | Setup: ★★★★★&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simplest implementation — but your schema (and potentially data samples) go to OpenAI:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&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;Schema: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Generate SQL for: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best accuracy&lt;/strong&gt; on complex queries. &lt;strong&gt;Worst privacy&lt;/strong&gt; — every schema call goes to OpenAI. Not suitable for sensitive databases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Privacy&lt;/th&gt;
&lt;th&gt;SQL Accuracy&lt;/th&gt;
&lt;th&gt;Setup Complexity&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VeilAnalytics&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;Browser CSV analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vanna.AI (local)&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;Production apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLCoder (Ollama)&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;High accuracy queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LlamaIndex&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;Python integrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outlines&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;Constrained output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4 Direct&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;Prototyping only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2025 Recommendation
&lt;/h2&gt;

&lt;p&gt;For &lt;strong&gt;maximum privacy + good accuracy:&lt;/strong&gt; Run SQLCoder via Ollama for the LLM layer, with DuckDB for the execution layer. Schema-only prompts mean no data rows leave your machine.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;zero setup + business users:&lt;/strong&gt; &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; in the browser — no installation, no accounts, no uploads.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;production apps with enterprise databases:&lt;/strong&gt; Vanna.AI with local ChromaDB + Ollama — the RAG approach learns your schema over time and improves accuracy.&lt;/p&gt;

&lt;p&gt;The open-source Text-to-SQL landscape in 2025 is genuinely mature enough to replace cloud tools for most use cases, with full data privacy. The performance gap with GPT-4 has closed significantly with fine-tuned models like SQLCoder.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — Natural language analytics for your data. In-browser, open, private.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>opensource</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Stop Uploading Your Excel Files to AI Tools — Here's the Private Alternative</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Thu, 17 Sep 2026 14:08:34 +0000</pubDate>
      <link>https://dev.to/veilanalytics/stop-uploading-your-excel-files-to-ai-tools-heres-the-private-alternative-2pk4</link>
      <guid>https://dev.to/veilanalytics/stop-uploading-your-excel-files-to-ai-tools-heres-the-private-alternative-2pk4</guid>
      <description>&lt;h2&gt;
  
  
  Stop Uploading Your Excel Files to AI Tools — Here's the Private Alternative
&lt;/h2&gt;

&lt;p&gt;You've probably done it. Opened ChatGPT, clicked the paperclip icon, and uploaded a spreadsheet to ask it questions.&lt;/p&gt;

&lt;p&gt;It works great. The problem is what just happened: your Excel file — with client names, revenue numbers, employee salaries, or supplier pricing — is now on OpenAI's servers. It's in their logs. It's subject to their data retention policies. And if OpenAI ever has a breach, your data is part of the exposure.&lt;/p&gt;

&lt;p&gt;For personal spreadsheets, maybe that's acceptable. For business data? It's a decision most people make without thinking about it.&lt;/p&gt;

&lt;p&gt;Here's what you can do instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens When You Upload to ChatGPT
&lt;/h2&gt;

&lt;p&gt;When you upload a file to ChatGPT's data analysis feature, OpenAI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Receives your file&lt;/strong&gt; on their servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stores it&lt;/strong&gt; for the duration of the conversation (and possibly longer for model training/safety, depending on your settings)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processes it&lt;/strong&gt; using a Python code execution environment on their infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs the interaction&lt;/strong&gt; including your data in their system logs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;OpenAI's privacy settings allow you to opt out of training data use, but the data still traverses and processes through their cloud infrastructure. For regulated industries (healthcare, finance, legal) or any company with a data handling policy, this creates real compliance exposure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Private Alternative: In-Browser Analytics
&lt;/h2&gt;

&lt;p&gt;The breakthrough that makes private Excel analytics possible: &lt;strong&gt;WebAssembly (WASM)&lt;/strong&gt; allows a full SQL engine to run inside your browser tab.&lt;/p&gt;

&lt;p&gt;Tools like &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; use DuckDB-WASM to process your file entirely within your browser's memory — the file is read from your local disk, never uploaded anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to verify this yourself:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open VeilAnalytics in Chrome&lt;/li&gt;
&lt;li&gt;Press F12 → Network tab&lt;/li&gt;
&lt;li&gt;Upload your Excel/CSV file&lt;/li&gt;
&lt;li&gt;Watch the Network tab — you'll see zero outbound requests to any server with your data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The data computation happens in your browser tab, using your CPU, against your local file. It's the equivalent of running Excel locally — except with natural language queries and SQL.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 How to Analyze Your Excel Files Privately
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Convert Excel to CSV (if needed)
&lt;/h3&gt;

&lt;p&gt;Most privacy-respecting tools work best with CSV. In Excel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File → Save As → CSV (Comma Delimited)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or use a local conversion:&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="c"&gt;# Using Python (runs locally, no upload)&lt;/span&gt;
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import pandas as pd
df = pd.read_excel('your_file.xlsx')
df.to_csv('your_file.csv', index=False)
print(df.head())
"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Ask Questions Without Uploading
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option A — VeilAnalytics (No code, browser-based):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;veilanalytics.netlify.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Drop your CSV file&lt;/li&gt;
&lt;li&gt;Ask: "What's the total revenue by region?" or "Show me the top 10 customers by order value"&lt;/li&gt;
&lt;li&gt;Get SQL results instantly — your file never left your computer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Option B — DuckDB Local (CLI/Python):&lt;/strong&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;

&lt;span class="c1"&gt;# Query the converted CSV natively (or install DuckDB's spatial extension for direct .xlsx)
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&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;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
  SELECT 
    region,
    SUM(revenue) as total_revenue,
    COUNT(*) as deals
  FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sales_report.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
  GROUP BY region
  ORDER BY total_revenue DESC
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;df&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option C — Ollama + Local Script (Natural Language to SQL):&lt;/strong&gt;&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&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="n"&gt;question&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="c1"&gt;# Get schema first (no actual data sent to LLM)
&lt;/span&gt;    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&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="nf"&gt;execute&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;CREATE VIEW data AS SELECT * FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;file_path&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="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&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;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DESCRIBE data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchdf&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Ask local LLM to generate SQL
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Runs 100% locally
&lt;/span&gt;        &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&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;Schema:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Write SQL to answer: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Return only SQL.&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&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;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="nf"&gt;fetchdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Example usage
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ask_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q3_sales.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What was total revenue by product category?&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔐 What Data Do These Tools Actually See?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What Server Receives&lt;/th&gt;
&lt;th&gt;Who Can Access It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT File Upload&lt;/td&gt;
&lt;td&gt;Your entire file&lt;/td&gt;
&lt;td&gt;OpenAI, potentially regulators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Gemini File&lt;/td&gt;
&lt;td&gt;Your entire file&lt;/td&gt;
&lt;td&gt;Google, potentially regulators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VeilAnalytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nothing (browser-only)&lt;/td&gt;
&lt;td&gt;Nobody — not even VeilAnalytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DuckDB Local&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;Nobody&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ollama Local&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;Nobody&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💼 Types of Files You Should Never Upload
&lt;/h2&gt;

&lt;p&gt;Some files should never touch a cloud AI server regardless of convenience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HR files&lt;/strong&gt; — employee salaries, performance reviews, headcount data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client lists&lt;/strong&gt; — names, emails, contract values (customer PII)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Financial projections&lt;/strong&gt; — unreleased revenue, pricing strategy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supplier data&lt;/strong&gt; — pricing agreements, contract terms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare exports&lt;/strong&gt; — patient identifiers, diagnosis codes, treatment history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal documents&lt;/strong&gt; — case files, NDAs, confidential agreements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For all of these, local-first analytics is the only responsible choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  What About Data Size Limits?
&lt;/h2&gt;

&lt;p&gt;In-browser DuckDB handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Up to ~500MB CSV&lt;/strong&gt; comfortably in most modern browsers (8GB RAM machines)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parquet files&lt;/strong&gt; much more efficiently (10x smaller than CSV for the same data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For larger files&lt;/strong&gt; — use DuckDB locally via Python or CLI, which has no memory limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For typical business Excel exports (sales reports, CRM exports, financial summaries), which are almost always under 100MB, browser-based processing is instant.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Habit to Build
&lt;/h2&gt;

&lt;p&gt;Every time you're about to upload a business file to an AI tool, ask one question first: &lt;strong&gt;"Would I be comfortable if this file appeared in a public breach disclosure?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the answer is no — and for most business files it should be — reach for a local-first tool instead. The analysis quality is comparable. The data risk is zero.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — Analyze your Excel and CSV files with natural language queries. 100% in your browser. Zero data uploads. Zero accounts required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>excel</category>
      <category>privacy</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>5 Private ChatGPT Alternatives for Analyzing Business Data (No Cloud Required)</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Wed, 16 Sep 2026 14:03:36 +0000</pubDate>
      <link>https://dev.to/veilanalytics/5-private-chatgpt-alternatives-for-analyzing-business-data-no-cloud-required-40lm</link>
      <guid>https://dev.to/veilanalytics/5-private-chatgpt-alternatives-for-analyzing-business-data-no-cloud-required-40lm</guid>
      <description>&lt;h2&gt;
  
  
  5 Private ChatGPT Alternatives for Analyzing Business Data (No Cloud Required)
&lt;/h2&gt;

&lt;p&gt;ChatGPT's data analysis feature is genuinely impressive. Drop in a CSV and ask questions in plain English — it works.&lt;/p&gt;

&lt;p&gt;The problem: your data goes to OpenAI's servers. For most business files — client lists, financial records, employee data, sales reports — that's a non-starter. OpenAI's data retention policies and your company's data handling requirements simply don't align.&lt;/p&gt;

&lt;p&gt;Here are 5 alternatives that give you the same conversational analytics experience with your data staying on your hardware.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. VeilAnalytics — In-Browser, Zero Upload
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Business users who want instant no-install analytics&lt;/p&gt;

&lt;p&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; runs DuckDB entirely inside your browser tab using WebAssembly. You load a CSV from your local disk, ask questions in natural language, and get results — all without your file ever touching a server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it's different:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your file loads directly from your disk into browser memory&lt;/li&gt;
&lt;li&gt;No backend, no API calls with your data&lt;/li&gt;
&lt;li&gt;Open the browser's Network tab while using it — you'll see zero upload requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business analysts and non-technical users&lt;/li&gt;
&lt;li&gt;Quick one-off file analysis&lt;/li&gt;
&lt;li&gt;Any situation where you can't install software&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Privacy model:&lt;/strong&gt; Maximum — computation is browser-sandboxed, nothing leaves your device.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Ollama + Open WebUI — Full Local LLM Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Developers and teams who want the full ChatGPT-like experience locally&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ollama.ai" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; lets you run large language models (Llama 3.1, Mistral, Qwen, etc.) entirely on your own hardware. Pair it with Open WebUI for a polished chat interface:&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="c"&gt;# Install Ollama&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.ai/install.sh | sh

&lt;span class="c"&gt;# Pull a model (runs 100% locally after download)&lt;/span&gt;
ollama pull llama3.1

&lt;span class="c"&gt;# Optional: Add Open WebUI for ChatGPT-like interface&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host ghcr.io/open-webui/open-webui:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For file analysis, Open WebUI supports uploading documents that get processed locally using the running LLM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teams that want a ChatGPT-quality interface hosted internally&lt;/li&gt;
&lt;li&gt;Technical teams comfortable with Docker/terminal&lt;/li&gt;
&lt;li&gt;Ongoing use with multiple users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Privacy model:&lt;/strong&gt; Complete — all processing on your hardware, all network calls stay internal.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. LlamaIndex + DuckDB — Custom Analytics Pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Developers building internal data analytics tools&lt;/p&gt;

&lt;p&gt;&lt;a href="https://llamaindex.ai" rel="noopener noreferrer"&gt;LlamaIndex&lt;/a&gt; provides a framework for building "ask questions about your documents" systems. Combined with DuckDB for structured data and a local Ollama model, you get a fully private analytics pipeline:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.llms.ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Ollama&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;

&lt;span class="c1"&gt;# Point to a local model
&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ollama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;300.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Connect to local DuckDB with your data
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;business_data.duckdb&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE TABLE IF NOT EXISTS sales AS SELECT * FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sales.csv&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Build SQL query engine
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duckdb:///business_data.duckdb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sql_db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core.query_engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NLSQLTableQueryEngine&lt;/span&gt;
&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NLSQLTableQueryEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql_database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sql_db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Ask questions in natural language
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What was our top-performing product category last month?&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teams building internal tools&lt;/li&gt;
&lt;li&gt;Multi-table, multi-file analytics&lt;/li&gt;
&lt;li&gt;Integrating into existing Python workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Privacy model:&lt;/strong&gt; Complete if using Ollama locally. Schema-only if using OpenAI BYOK.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Jan.ai — Desktop ChatGPT Replacement
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Non-technical users who want a desktop app&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jan.ai" rel="noopener noreferrer"&gt;Jan.ai&lt;/a&gt; is an open-source desktop application that lets you download and run LLMs locally with a polished UI. Think of it as "ChatGPT, but it lives on your computer."&lt;/p&gt;

&lt;p&gt;It includes file upload capabilities for document Q&amp;amp;A, and all processing happens on-device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download from jan.ai (available for Mac, Windows, Linux)&lt;/li&gt;
&lt;li&gt;Download a model (Mistral, Llama 3, Phi-3)&lt;/li&gt;
&lt;li&gt;Start chatting and uploading files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less technical users who want a simple install&lt;/li&gt;
&lt;li&gt;Individual contributors who want private file Q&amp;amp;A&lt;/li&gt;
&lt;li&gt;No coding required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Privacy model:&lt;/strong&gt; Complete — everything runs locally in the desktop app.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Fabric + Local Model — The Power User Option
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Developers who want composable AI patterns for data workflows&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/danielmiessler/fabric" rel="noopener noreferrer"&gt;Fabric&lt;/a&gt; is an open-source CLI framework for augmenting human capabilities with AI. It can be configured to use local models through Ollama and includes patterns specifically for data analysis:&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="c"&gt;# Install Fabric&lt;/span&gt;
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/danielmiessler/fabric@latest

&lt;span class="c"&gt;# Configure to use local Ollama&lt;/span&gt;
fabric &lt;span class="nt"&gt;--setup&lt;/span&gt;
&lt;span class="c"&gt;# Select Ollama as provider, set model to llama3.1&lt;/span&gt;

&lt;span class="c"&gt;# Analyze a CSV by piping it through a pattern&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;sales_data.csv | fabric &lt;span class="nt"&gt;-p&lt;/span&gt; analyze_data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Command-line power users&lt;/li&gt;
&lt;li&gt;Automated batch analysis pipelines&lt;/li&gt;
&lt;li&gt;Chaining AI steps in shell scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Privacy model:&lt;/strong&gt; Complete with local Ollama. Highly customizable patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Technical Level&lt;/th&gt;
&lt;th&gt;Setup Time&lt;/th&gt;
&lt;th&gt;File Support&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VeilAnalytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None needed&lt;/td&gt;
&lt;td&gt;0 min (just open browser)&lt;/td&gt;
&lt;td&gt;CSV, Parquet&lt;/td&gt;
&lt;td&gt;Business users, quick analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ollama + Open WebUI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;15 min&lt;/td&gt;
&lt;td&gt;Docs, text&lt;/td&gt;
&lt;td&gt;Teams wanting ChatGPT locally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LlamaIndex + DuckDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;1-2 hours&lt;/td&gt;
&lt;td&gt;CSV, databases&lt;/td&gt;
&lt;td&gt;Custom analytics tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jan.ai&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;td&gt;Docs, text&lt;/td&gt;
&lt;td&gt;Non-technical desktop users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fabric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (CLI)&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;td&gt;Any via pipe&lt;/td&gt;
&lt;td&gt;Power users, pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Which One Should You Use?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I just want to analyze a CSV without uploading it:&lt;/strong&gt;&lt;br&gt;
→ &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — open the browser, drop the file, ask questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I want a ChatGPT replacement for my whole team:&lt;/strong&gt;&lt;br&gt;
→ Ollama + Open WebUI — self-host it on a team server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm building an internal data tool for my company:&lt;/strong&gt;&lt;br&gt;
→ LlamaIndex + DuckDB + Ollama — fully programmable, fully private.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I want a desktop app with no technical setup:&lt;/strong&gt;&lt;br&gt;
→ Jan.ai — install and go.&lt;/p&gt;




&lt;p&gt;The "send data to OpenAI" approach isn't the only path to conversational analytics. All five of these tools let you ask questions about your business data without your data leaving your control.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — The easiest private data analytics tool. No uploads, no accounts, no server. Runs 100% in your browser.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>privacy</category>
      <category>database</category>
      <category>productivity</category>
    </item>
    <item>
      <title>SQL Analytics in the Browser Without Uploading Your Data Anywhere</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Tue, 15 Sep 2026 14:11:50 +0000</pubDate>
      <link>https://dev.to/veilanalytics/sql-analytics-in-the-browser-without-uploading-your-data-anywhere-153g</link>
      <guid>https://dev.to/veilanalytics/sql-analytics-in-the-browser-without-uploading-your-data-anywhere-153g</guid>
      <description>&lt;h2&gt;
  
  
  SQL Analytics in the Browser Without Uploading Your Data Anywhere
&lt;/h2&gt;

&lt;p&gt;Every time you use a data tool that asks you to "upload your file," your data leaves your machine.&lt;/p&gt;

&lt;p&gt;It goes to a server, gets processed, gets stored (often indefinitely in logs), and becomes subject to that company's data retention, breach risk, and subpoena exposure.&lt;/p&gt;

&lt;p&gt;For personal finance spreadsheets, HR data, client lists, or internal business data — this is a real problem that most people don't think about until something goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is now a better way: SQL analytics that runs entirely inside your browser tab.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 How In-Browser SQL Actually Works
&lt;/h2&gt;

&lt;p&gt;Modern browsers can run WebAssembly (WASM) — compiled binary code that executes at near-native speed inside the browser sandbox. This means a full SQL engine can run client-side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DuckDB-WASM&lt;/strong&gt; is exactly this: DuckDB, the fast columnar analytical database, compiled to WebAssembly. It runs completely inside your browser with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No network requests for computation&lt;/li&gt;
&lt;li&gt;No backend server&lt;/li&gt;
&lt;li&gt;No data ever leaving your device&lt;/li&gt;
&lt;li&gt;Full SQL support: JOINs, GROUP BY, window functions, CTEs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Browser Tab
├── DuckDB-WASM (SQL engine, runs in Web Worker)
├── Your CSV/Parquet file (loaded from local disk, stays local)
└── Results displayed in your browser

                ↕ Network traffic: ZERO (after initial page load)

No backend servers. No uploads. No logs on someone else's machine.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔧 What You Can Query
&lt;/h2&gt;

&lt;p&gt;In-browser DuckDB handles all standard analytical SQL:&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="c1"&gt;-- Aggregate across a million-row CSV&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revenue&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;total_revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&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;order_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revenue&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;avg_order&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'sales_data.csv'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;category&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;total_revenue&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Join two local files&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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;order_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'customers.csv'&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="s1"&gt;'orders.csv'&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Window functions work too&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&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="k"&gt;month&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;cumulative_revenue&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'monthly_sales.csv'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📂 Supported File Formats
&lt;/h2&gt;

&lt;p&gt;DuckDB-WASM can query:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CSV&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sales.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-detects delimiter and types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TSV&lt;/td&gt;
&lt;td&gt;&lt;code&gt;export.tsv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tab-delimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parquet&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data.parquet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fastest — columnar format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;&lt;code&gt;records.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrays of objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XLSX&lt;/td&gt;
&lt;td&gt;Via conversion&lt;/td&gt;
&lt;td&gt;Load with js-xlsx first&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚡ Performance in the Browser
&lt;/h2&gt;

&lt;p&gt;You'd expect browser execution to be slow. It's not.&lt;/p&gt;

&lt;p&gt;For analytical queries (aggregations, joins, filters), DuckDB-WASM typically handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1M rows&lt;/strong&gt; → under 500ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10M rows&lt;/strong&gt; → 2-5 seconds
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset Size Limits:&lt;/strong&gt; Modern browsers enforce a 32-bit WebAssembly address ceiling (~2GB to 4GB memory buffer in Chrome/Firefox). For files up to ~1GB–2GB (or Parquet files up to 10M+ rows), in-browser processing is instant. For massive multi-gigabyte files, running DuckDB in-process via local Python/CLI is recommended.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Using It Yourself
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Use VeilAnalytics (No Code Required)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; provides an out-of-the-box natural language interface on top of in-browser DuckDB. You:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the app (no login required)&lt;/li&gt;
&lt;li&gt;Drop your CSV or upload from disk&lt;/li&gt;
&lt;li&gt;Ask your question in plain English: "What's the total revenue by region for Q3?"&lt;/li&gt;
&lt;li&gt;Get results instantly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your file never leaves your browser. There are no servers processing your data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Build Your Own with DuckDB-WASM
&lt;/h3&gt;

&lt;p&gt;If you're a developer, you can integrate DuckDB-WASM into any web application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;duckdb&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@latest/dist/duckdb-browser-mvp.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MANUAL_BUNDLES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;mvp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;mainModule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@latest/dist/duckdb-mvp.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;mainWorker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@latest/dist/duckdb-browser-mvp.worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectBundle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MANUAL_BUNDLES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mainWorker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncDuckDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mainModule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle file upload&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fileInput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerFileBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="c1"&gt;// Query it immediately&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' LIMIT 10`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fileInput"&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;".csv,.parquet"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file is loaded from your local disk directly into the browser's memory — no fetch request, no upload endpoint, no server.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 The Privacy Guarantees
&lt;/h2&gt;

&lt;p&gt;When you run analytics in-browser with DuckDB-WASM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No upload API call&lt;/strong&gt; — the file never leaves your device&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No server logs&lt;/strong&gt; — there's no server to log anything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cookies/tracking&lt;/strong&gt; needed for the computation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works offline&lt;/strong&gt; — after the initial page load, it can function without internet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser sandboxed&lt;/strong&gt; — WASM runs in an isolated sandbox, can't access other files or network&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Verify this yourself: open your browser's Network tab and run a query on a local file. You'll see zero outbound requests to any analytics server.&lt;/p&gt;




&lt;h2&gt;
  
  
  🆚 How This Compares
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Data Uploaded?&lt;/th&gt;
&lt;th&gt;Privacy&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google Sheets&lt;/td&gt;
&lt;td&gt;✅ To Google servers&lt;/td&gt;
&lt;td&gt;❌ Low&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Excel Online&lt;/td&gt;
&lt;td&gt;✅ To Microsoft&lt;/td&gt;
&lt;td&gt;❌ Low&lt;/td&gt;
&lt;td&gt;Subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT File Upload&lt;/td&gt;
&lt;td&gt;✅ To OpenAI&lt;/td&gt;
&lt;td&gt;❌ Low&lt;/td&gt;
&lt;td&gt;$20/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tableau Online&lt;/td&gt;
&lt;td&gt;✅ To Salesforce&lt;/td&gt;
&lt;td&gt;❌ Low&lt;/td&gt;
&lt;td&gt;$$$$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VeilAnalytics (in-browser DuckDB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Never&lt;/td&gt;
&lt;td&gt;✅ Maximum&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DIY DuckDB-WASM&lt;/td&gt;
&lt;td&gt;❌ Never&lt;/td&gt;
&lt;td&gt;✅ Maximum&lt;/td&gt;
&lt;td&gt;Dev time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Browser-native SQL analytics isn't just a privacy feature. It's also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster to start&lt;/strong&gt; — no account, no upload, instant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheaper to operate&lt;/strong&gt; — no backend infrastructure costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simpler architecture&lt;/strong&gt; — static frontend, zero servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More scalable&lt;/strong&gt; — computation runs on each user's device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your use case involves sensitive files and analytical SQL queries, in-browser DuckDB is worth understanding. The technology is mature, fast enough for most real-world datasets, and provides privacy guarantees that no server-based tool can match.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — In-browser SQL analytics with natural language queries. Your data stays in your browser. Always.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>database</category>
      <category>privacy</category>
      <category>javascript</category>
    </item>
    <item>
      <title>HIPAA-Compliant AI Analytics: The Case for 100% Local Data Processing</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Mon, 14 Sep 2026 15:41:41 +0000</pubDate>
      <link>https://dev.to/veilanalytics/hipaa-compliant-ai-analytics-the-case-for-100-local-data-processing-g3</link>
      <guid>https://dev.to/veilanalytics/hipaa-compliant-ai-analytics-the-case-for-100-local-data-processing-g3</guid>
      <description>&lt;h2&gt;
  
  
  HIPAA-Compliant AI Analytics: The Case for 100% Local Data Processing
&lt;/h2&gt;

&lt;p&gt;Healthcare and fintech teams are stuck in an impossible position.&lt;/p&gt;

&lt;p&gt;Business stakeholders want AI-powered analytics: "Why can't we just ask it questions about our patient data?" But compliance officers shut it down instantly: "That data never leaves our network."&lt;/p&gt;

&lt;p&gt;The standard answer has been: wait for your IT team to procure an expensive enterprise AI platform, negotiate data processing agreements, get security reviews — a process that takes 6-18 months.&lt;/p&gt;

&lt;p&gt;There's a faster path: &lt;strong&gt;local-first AI analytics architecture&lt;/strong&gt; where the AI never touches the data at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏥 The HIPAA Problem With Cloud AI
&lt;/h2&gt;

&lt;p&gt;The fundamental issue: most AI analytics tools (ChatGPT data analysis, Google Gemini, Claude file uploads) work by &lt;strong&gt;sending your data to a third-party server&lt;/strong&gt; for processing.&lt;/p&gt;

&lt;p&gt;Under HIPAA, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need a signed &lt;strong&gt;Business Associate Agreement (BAA)&lt;/strong&gt; with every vendor&lt;/li&gt;
&lt;li&gt;Data transfers must be encrypted in transit AND at rest on their servers&lt;/li&gt;
&lt;li&gt;You're liable for any breach that occurs on their infrastructure&lt;/li&gt;
&lt;li&gt;Patient data is now subject to their data retention policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most AI vendors offer BAAs for enterprise tiers ($$$). But the fundamental problem remains: &lt;strong&gt;your raw patient records are sitting on someone else's servers&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 The Local Architecture: AI for Schema, Compute for Data
&lt;/h2&gt;

&lt;p&gt;The insight that changes everything: &lt;strong&gt;you don't need to send the data to the AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You only need to send the AI two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your &lt;strong&gt;table schema&lt;/strong&gt; (column names and data types — not real data)&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;natural language question&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AI returns a SQL query. That query runs &lt;strong&gt;100% locally&lt;/strong&gt; against your actual data. The AI never sees a single patient name, diagnosis code, or financial record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHAT THE AI SEES:
  Table: patient_vitals
  Columns: patient_id (INT), recorded_at (TIMESTAMP),
           systolic_bp (INT), diastolic_bp (INT), bmi (FLOAT)

  Question: "Show me patients with BMI over 30 recorded last quarter"

WHAT THE AI RETURNS:
  SELECT patient_id, AVG(bmi) as avg_bmi
  FROM patient_vitals
  WHERE bmi &amp;gt; 30
    AND recorded_at &amp;gt;= CURRENT_DATE - INTERVAL 90 DAYS
  GROUP BY patient_id

WHAT RUNS LOCALLY:
  That SQL query → against your actual database → results stay on-premise
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ Implementation: Zero-PHI Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Fully Local (Ollama + DuckDB)
&lt;/h3&gt;

&lt;p&gt;Run the LLM entirely on-premise using &lt;a href="https://ollama.ai" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; with an open-source model:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask_your_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema_description&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="n"&gt;question&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="n"&gt;db_path&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Generates SQL from natural language using local Ollama.
    Zero data leaves your network. Ever.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&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;You are a SQL expert. Generate a safe read-only SQL query.

Schema:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;schema_description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Rules:
- Only SELECT statements (no INSERT, UPDATE, DELETE, DROP)
- Return only the SQL, no explanation
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Runs 100% locally
&lt;/span&gt;        &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Execute locally against DuckDB in read-only mode to prevent write/drop injections
&lt;/span&gt;    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;read_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&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;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="nf"&gt;fetchdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;row_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;&lt;strong&gt;Compliance posture:&lt;/strong&gt; Zero BAA needed. AI runs locally, data never leaves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: BYOK (Bring Your Own Key) with Schema-Only
&lt;/h3&gt;

&lt;p&gt;If you need a more capable model (GPT-4, Claude), you can still maintain compliance by sending only schema metadata — never real data:&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;build_schema_context&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;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DuckDBPyConnection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Extract schema only — no actual data values.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="o"&gt;=&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;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SHOW TABLES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;schema_parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&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;execute&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;DESCRIBE &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="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;col_defs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;column_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;column_type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;schema_parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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;Table &lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;col_defs&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="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema_parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Only schema goes to OpenAI — not your patient data
&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_schema_context&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;sql_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ask_openai_for_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&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;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql_query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Local execution
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📋 HIPAA Compliance Checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Local Architecture&lt;/th&gt;
&lt;th&gt;Cloud AI (typical)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PHI stays on-premise&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BAA with AI vendor required&lt;/td&gt;
&lt;td&gt;✅ Not needed&lt;/td&gt;
&lt;td&gt;⚠️ Required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data encrypted in AI provider's cloud&lt;/td&gt;
&lt;td&gt;✅ N/A&lt;/td&gt;
&lt;td&gt;⚠️ Dependent on vendor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit trail of all data access&lt;/td&gt;
&lt;td&gt;✅ Full local logs&lt;/td&gt;
&lt;td&gt;⚠️ Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works without internet&lt;/td&gt;
&lt;td&gt;✅ Fully offline&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance officer sign-off difficulty&lt;/td&gt;
&lt;td&gt;✅ Low&lt;/td&gt;
&lt;td&gt;❌ High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🏢 Who This Architecture Is For
&lt;/h2&gt;

&lt;p&gt;This approach is ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare providers&lt;/strong&gt; — patient records, clinical data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health insurance companies&lt;/strong&gt; — claims data, member records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fintech/banking&lt;/strong&gt; — transaction data, PII, KYC records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal firms&lt;/strong&gt; — client records and case data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Government agencies&lt;/strong&gt; — citizen data, law enforcement records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's equally useful for any team that moves fast but needs data governance — internal business analysts who want to ask questions about sensitive sales, HR, or financial data without IT procurement cycles.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Browser-Native: The Next Frontier
&lt;/h2&gt;

&lt;p&gt;The most privacy-preserving option of all: analytics that runs directly in the browser with &lt;strong&gt;no backend server&lt;/strong&gt; at all.&lt;/p&gt;

&lt;p&gt;Newer tools like &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; run DuckDB as WebAssembly directly in your browser tab. You upload a CSV, ask a question in natural language, and the query runs client-side. The file never touches a server.&lt;/p&gt;

&lt;p&gt;For small to medium datasets (up to a few hundred MB), this approach provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero infrastructure cost&lt;/li&gt;
&lt;li&gt;Zero compliance burden&lt;/li&gt;
&lt;li&gt;Instant results&lt;/li&gt;
&lt;li&gt;Works offline after first load&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You don't have to choose between "powerful AI analytics" and "HIPAA compliance." The local-first architecture gives you both by separating the AI's role (SQL generation from schema) from the data's role (local computation).&lt;/p&gt;

&lt;p&gt;The result: your compliance officers say yes, your business analysts get their natural language queries, and your patient data never leaves the building.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — Ask questions about your data in natural language. Runs 100% in your browser. Zero data uploads.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>database</category>
      <category>privacy</category>
    </item>
    <item>
      <title>DuckDB vs Pandas: Why Your Analytics Script Runs 10x Faster After One Switch</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:41:20 +0000</pubDate>
      <link>https://dev.to/veilanalytics/duckdb-vs-pandas-why-your-analytics-script-runs-10x-faster-after-one-switch-1hhh</link>
      <guid>https://dev.to/veilanalytics/duckdb-vs-pandas-why-your-analytics-script-runs-10x-faster-after-one-switch-1hhh</guid>
      <description>&lt;h2&gt;
  
  
  DuckDB vs Pandas: Why Your Analytics Script Runs 10x Faster After One Switch
&lt;/h2&gt;

&lt;p&gt;If you've ever waited minutes for a pandas &lt;code&gt;.groupby()&lt;/code&gt; on a 2M row CSV, you know the frustration.&lt;/p&gt;

&lt;p&gt;The good news: there's a drop-in alternative that uses SQL syntax, runs in-process with zero setup, and regularly outperforms pandas by &lt;strong&gt;5x to 50x&lt;/strong&gt; on analytical queries — &lt;strong&gt;DuckDB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn't theoretical. Let's run real benchmarks.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 The Benchmark Setup
&lt;/h2&gt;

&lt;p&gt;Both libraries tested on the same machine with the same 5M row dataset (simulated e-commerce orders, ~1.2 GB CSV):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MacBook M2 Pro, 16GB RAM&lt;/li&gt;
&lt;li&gt;Python 3.11&lt;/li&gt;
&lt;li&gt;pandas 2.2, DuckDB 0.10
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orders_5m.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# pandas approach
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;sort_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ascending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&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;pandas: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# DuckDB approach
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;con&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
  SELECT category, SUM(revenue) as total
  FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
  GROUP BY category
  ORDER BY total DESC
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;df&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;DuckDB: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📊 Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;pandas&lt;/th&gt;
&lt;th&gt;DuckDB&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read 5M row CSV&lt;/td&gt;
&lt;td&gt;18.4s&lt;/td&gt;
&lt;td&gt;1.2s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GROUP BY + SUM&lt;/td&gt;
&lt;td&gt;4.1s&lt;/td&gt;
&lt;td&gt;0.3s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JOIN two 2M row tables&lt;/td&gt;
&lt;td&gt;22.7s&lt;/td&gt;
&lt;td&gt;1.8s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter + Aggregate&lt;/td&gt;
&lt;td&gt;6.3s&lt;/td&gt;
&lt;td&gt;0.4s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Parquet (columnar)&lt;/td&gt;
&lt;td&gt;3.2s&lt;/td&gt;
&lt;td&gt;0.09s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;36x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DuckDB won every single test&lt;/strong&gt;, with the largest gap on columnar Parquet reads — which is exactly how modern data lakes store data.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Note on Threading &amp;amp; PyArrow:&lt;/strong&gt;&lt;br&gt;
The timings above measure out-of-the-box defaults: DuckDB automatically parallelizes across all CPU cores (8 worker threads on M2 Pro), whereas default &lt;code&gt;pd.read_csv()&lt;/code&gt; runs on a single-threaded C parser. Specifying &lt;code&gt;engine="pyarrow"&lt;/code&gt; in pandas enables multi-threaded ingestion and noticeably narrows the CSV read gap, though DuckDB retains a significant advantage during complex joins and in-memory aggregations.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔍 Why Is DuckDB So Much Faster?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Columnar Storage Engine
&lt;/h3&gt;

&lt;p&gt;pandas stores data &lt;strong&gt;row-by-row&lt;/strong&gt; in memory. When you do a &lt;code&gt;SUM(revenue)&lt;/code&gt;, it still reads every column of every row even though only one column matters.&lt;/p&gt;

&lt;p&gt;DuckDB uses a &lt;strong&gt;columnar vectorized engine&lt;/strong&gt; — it reads only the columns your query touches, in parallel 64-element chunks using SIMD CPU instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Query Compilation + Parallelism
&lt;/h3&gt;

&lt;p&gt;DuckDB compiles each query to an optimized execution plan and automatically parallelizes across all CPU cores. pandas is single-threaded by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Out-of-Core Query Execution
&lt;/h3&gt;

&lt;p&gt;DuckDB can query files &lt;strong&gt;larger than RAM&lt;/strong&gt; by streaming chunks. pandas &lt;code&gt;read_csv()&lt;/code&gt; loads everything into memory first — if your CSV is 32GB, you need 32GB+ RAM.&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="c1"&gt;# This works in DuckDB even if the file is 50GB and you have 8GB RAM
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;huge_file.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; WHERE amount &amp;gt; 1000 LIMIT 100&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;df&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# pandas would crash or swap to disk with OOM errors
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ Practical Migration: pandas → DuckDB
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before (pandas):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sales.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;monthly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&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;2024&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;month&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After (DuckDB):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;
&lt;span class="n"&gt;monthly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
  SELECT month, SUM(amount) as total
  FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sales.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
  WHERE year = 2024
  GROUP BY month
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;df&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a regular pandas DataFrame — you can still use all your downstream pandas/matplotlib code unchanged.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 The Privacy Angle: Why DuckDB Fits Local Analytics
&lt;/h2&gt;

&lt;p&gt;When your analytics process customer data, you don't want it leaving your environment. DuckDB is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero external calls&lt;/strong&gt; — pure in-process computation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No server setup&lt;/strong&gt; — runs inside your Python or Node.js process&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WASM version&lt;/strong&gt; — runs directly in the browser with no backend at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly the architecture that tools like &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; use to power in-browser SQL analytics on sensitive files — your data never leaves the browser tab.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 When Should You Stick With pandas?
&lt;/h2&gt;

&lt;p&gt;DuckDB is not always the answer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Better Tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Complex row-by-row transformations&lt;/td&gt;
&lt;td&gt;pandas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML preprocessing pipelines (sklearn)&lt;/td&gt;
&lt;td&gt;pandas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics on 10M+ rows / large joins&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DuckDB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reading Parquet/CSV files&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DuckDB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL-familiar team doing data analysis&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DuckDB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running in the browser (WASM)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DuckDB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔧 Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;duckdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No Docker, no server, no config. Import and query:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;

&lt;span class="c1"&gt;# Query any CSV, Parquet, or JSON file directly
&lt;/span&gt;&lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; LIMIT 5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;If your analytics code runs on files larger than a few hundred thousand rows, &lt;strong&gt;switching the aggregation layer from pandas to DuckDB is the single highest-ROI change you can make&lt;/strong&gt; — typically a 10–20x speedup for less than 30 minutes of work.&lt;/p&gt;

&lt;p&gt;The API is SQL, the output is a DataFrame, and the setup is one &lt;code&gt;pip install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Try it on your slowest script today.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;VeilAnalytics&lt;/a&gt; — In-browser SQL analytics with zero data uploads. Built on DuckDB-WASM.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>datascience</category>
      <category>performance</category>
    </item>
    <item>
      <title>Private ChatGPT Alternative for SQL Analytics on Sensitive Enterprise Data</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Mon, 07 Sep 2026 14:35:15 +0000</pubDate>
      <link>https://dev.to/veilanalytics/private-chatgpt-alternative-for-sql-analytics-on-sensitive-enterprise-data-2bef</link>
      <guid>https://dev.to/veilanalytics/private-chatgpt-alternative-for-sql-analytics-on-sensitive-enterprise-data-2bef</guid>
      <description>&lt;h2&gt;
  
  
  Private ChatGPT Alternative for SQL Analytics on Sensitive Enterprise Data
&lt;/h2&gt;

&lt;p&gt;When data analysts use ChatGPT or Claude to write SQL queries or analyze spreadsheets, they often copy-paste sensitive customer records, financial figures, or Protected Health Information (PHI) directly into the prompt box.&lt;/p&gt;

&lt;p&gt;This creates severe security vulnerabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data Retention Risks:&lt;/strong&gt; AI providers may retain prompt data for model training.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance Violations:&lt;/strong&gt; Breach of HIPAA, SOC2, or GDPR data residency mandates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Leaks:&lt;/strong&gt; Accidental exposure of internal business intelligence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how we built a &lt;strong&gt;Private ChatGPT Alternative for SQL Analytics&lt;/strong&gt; that guarantees zero raw-data transmission.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡️ The Zero-Raw-Data AI Workstation Model
&lt;/h2&gt;

&lt;p&gt;Traditional AI analytics platforms upload your dataset to cloud servers. VeilAnalytics flips this paradigm by using an &lt;strong&gt;air-gapped, local-first compute engine&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ User Prompt: "Show monthly churn rate" ]
                   │
                   ▼
[ Schema Extractor (Metadata Only) ]
                   │
                   ▼
[ Local LLM / Air-Gapped API ] ──► Returns Raw SQL SELECT Query
                                           │
                                           ▼
[ AST Guardrail ] ─────────────────► Blocks DELETE/DROP/INJECTION
                                           │
                                           ▼
[ In-Process DuckDB Engine ] ──────► Computes Result In-Memory
                                           │
                                           ▼
[ Client Workspace ] ──────────────► Renders Table &amp;amp; Chart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔑 Key Pillars of Zero-Trust AI Analytics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Air-Gapped Metadata Prompts
&lt;/h3&gt;

&lt;p&gt;The AI model only receives the database structure (column names like &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;signup_date&lt;/code&gt;, &lt;code&gt;plan_type&lt;/code&gt;). It &lt;strong&gt;never&lt;/strong&gt; receives actual customer names, credit card numbers, or medical records.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AST Query Filtering
&lt;/h3&gt;

&lt;p&gt;Every AI-generated SQL query is parsed into an Abstract Syntax Tree (AST) before execution. Non-&lt;code&gt;SELECT&lt;/code&gt; statements or attempts to access host file systems are terminated instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Local DuckDB Columnar Performance
&lt;/h3&gt;

&lt;p&gt;Analytical queries execute locally using DuckDB's C++ engine, delivering sub-second response times across gigabyte-scale datasets.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Try It Live
&lt;/h2&gt;

&lt;p&gt;See how zero-raw-data AI analytics works in your browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://veil-analytics.onrender.com" rel="noopener noreferrer"&gt;veil-analytics.onrender.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Official Website:&lt;/strong&gt; &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;veilanalytics.netlify.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>database</category>
      <category>privacy</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Query CSV Files with Natural Language Locally (No Cloud Uploads)</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Sat, 05 Sep 2026 14:50:13 +0000</pubDate>
      <link>https://dev.to/veilanalytics/how-to-query-csv-files-with-natural-language-locally-no-cloud-uploads-3fem</link>
      <guid>https://dev.to/veilanalytics/how-to-query-csv-files-with-natural-language-locally-no-cloud-uploads-3fem</guid>
      <description>&lt;h2&gt;
  
  
  How to Query CSV Files with Natural Language Locally (No Cloud Uploads)
&lt;/h2&gt;

&lt;p&gt;If you are a data analyst or software engineer, querying multi-gigabyte CSV files usually requires one of two annoying paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Slow Excel / Sheet UIs:&lt;/strong&gt; Crashing when loading more than 1,000,000 rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud AI SaaS Tools:&lt;/strong&gt; Uploading raw customer files to cloud servers with privacy &amp;amp; GDPR risks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this guide, we will show you how to query CSV files in natural language &lt;strong&gt;100% locally&lt;/strong&gt; using an in-process &lt;strong&gt;DuckDB C++ engine&lt;/strong&gt; and AST security validation.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Step 1: In-Process CSV Ingestion with DuckDB
&lt;/h2&gt;

&lt;p&gt;Unlike traditional SQL databases that require running heavy database servers (like PostgreSQL or MySQL), DuckDB operates as an in-process columnar database inside Node.js or Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;duckdb&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;duckdb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:memory:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Query 100MB+ CSV directly in-memory without database import&lt;/span&gt;
&lt;span class="nx"&gt;con&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM read_csv_auto('sales_data.csv') LIMIT 10;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Query Results:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;h2&gt;
  
  
  🔒 Step 2: Natural Language Text-to-SQL with Local Schema Injection
&lt;/h2&gt;

&lt;p&gt;To allow users to ask questions like &lt;em&gt;"What were our top 5 revenue states last month?"&lt;/em&gt;, we convert natural language into SQL using &lt;strong&gt;Schema-Only Context&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tableName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sales_data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VARCHAR"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"revenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DOUBLE"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TIMESTAMP"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the column names above are passed to the LLM (Ollama or BYOK endpoint). Raw data rows never leave your environment!&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Step 3: Local Visualization &amp;amp; Offline Dashboard Export
&lt;/h2&gt;

&lt;p&gt;Once DuckDB executes the SQL query locally, the resulting rows are rendered directly on the client using interactive Chart.js visualizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Instant Execution:&lt;/strong&gt; Query millions of rows in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🛡️ 100% Data Residency:&lt;/strong&gt; Zero raw-data transmission over the web.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📁 1-Click HTML Exporter:&lt;/strong&gt; Export standalone HTML charts that open offline in any browser.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Experience It Live
&lt;/h2&gt;

&lt;p&gt;Try this exact architecture live on our web demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://veil-analytics.onrender.com" rel="noopener noreferrer"&gt;veil-analytics.onrender.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Official Website:&lt;/strong&gt; &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;veilanalytics.netlify.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>database</category>
      <category>python</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Text to SQL Without Sending Data to OpenAI (Local DuckDB &amp; Ollama Setup)</title>
      <dc:creator>VeilAnalytics</dc:creator>
      <pubDate>Sat, 05 Sep 2026 06:52:57 +0000</pubDate>
      <link>https://dev.to/veilanalytics/text-to-sql-without-sending-data-to-openai-local-duckdb-ollama-setup-1mn7</link>
      <guid>https://dev.to/veilanalytics/text-to-sql-without-sending-data-to-openai-local-duckdb-ollama-setup-1mn7</guid>
      <description>&lt;h2&gt;
  
  
  Text to SQL Without Sending Data to OpenAI (Local DuckDB &amp;amp; Ollama Setup)
&lt;/h2&gt;

&lt;p&gt;When developers build Natural Language to SQL features for enterprise applications, the default approach is sending raw schema and table samples over an API to OpenAI or cloud LLMs.&lt;/p&gt;

&lt;p&gt;For security-conscious data teams handling HIPAA, GDPR, or sensitive customer data, sending raw files to third-party APIs creates immediate compliance friction.&lt;/p&gt;

&lt;p&gt;In this guide, we break down how to achieve &lt;strong&gt;Text-to-SQL analytics locally&lt;/strong&gt; with zero-raw-data exposure.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ The Architecture: Local DuckDB + Isolated Schema Ingestion
&lt;/h2&gt;

&lt;p&gt;Instead of passing entire CSV or database contents to a cloud API, we separate &lt;strong&gt;Schema Context&lt;/strong&gt; from &lt;strong&gt;Data Compute&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schema-Only Context:&lt;/strong&gt; Only table metadata (column names and data types) is supplied to the LLM to generate standard SQL queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Process Compute:&lt;/strong&gt; The generated SQL query runs 100% locally against &lt;strong&gt;DuckDB&lt;/strong&gt; in-memory compute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Raw-Data Transmission:&lt;/strong&gt; Raw data rows never leave your hardware.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Natural Language Question ] 
             │
             ▼
[ Local LLM (Ollama) / BYOK Provider ] ──► Generates SELECT Query
                                                  │
                                                  ▼
[ AST Security Sanitizer ] ───────────────► Validates Read-Only SQL
                                                  │
                                                  ▼
[ Local DuckDB Engine ] ──────────────────► In-Memory Execution
                                                  │
                                                  ▼
[ Local React Data Grid &amp;amp; Chart ] ────────► Rendered on Client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔒 AST SQL Security Guardrail
&lt;/h2&gt;

&lt;p&gt;Before any generated query touches the DuckDB database engine, it must pass through an Abstract Syntax Tree (AST) validation filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-sql-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;astify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stmt&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Only SELECT queries are allowed.&lt;/span&gt;&lt;span class="dl"&gt;'&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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;h2&gt;
  
  
  🚀 Try It Live
&lt;/h2&gt;

&lt;p&gt;We built &lt;strong&gt;VeilAnalytics&lt;/strong&gt; around this exact zero-raw-data architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://veil-analytics.onrender.com" rel="noopener noreferrer"&gt;veil-analytics.onrender.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Official Website:&lt;/strong&gt; &lt;a href="https://veilanalytics.netlify.app" rel="noopener noreferrer"&gt;veilanalytics.netlify.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>privacy</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
