# Wie man große CSV-Dateien mit lokalen LLMs analysiert

<!--category-- AI, LLM, Data Analysis, DuckDB, C#, Ollama -->
<datetime class="hidden">2025-12-18T10:00</datetime>

**Serie: Lokale LLMs für Daten**

Hier ist der Fehler, den jeder macht..... sie versuchen ihren CSV in einen LLM... **LLMs sollten Abfragen erzeugen., konsumieren keine Daten.**

Sie haben eine CSV-Datei von 500,MB und möchten fragen, was ', den durchschnittlichen Bestellwert nach Region, ?", Werkzeuge wie [Copilot in Excel](https://support.microsoft.com/en-gb/copilot-excel) kann dies tun, aber was, wenn Ihre Daten zu sensitiv für Cloud-Services sind.

Dieser Artikel zeigt Ihnen, wie Sie vor Ort, ,... privat, in C, #....

> Verwenden Sie DuckDB, um CSV-Dateien direkt abzufragen.Verwenden Sie ein lokales LLM, um die SQL zu generieren.Das LLM sieht nie Ihre Daten.

Für eine komplementäre Behandlung mit dem LLM-Interface ( und zeigt ein vollständiges Tool zur Umsetzung dieser Ideen.-, Profiling - Sicherer SQL-Modus.,, synthetisches Klonen und Drift-Detektion, siehe den Begleitartikel :. **[DatenSummarizer-:-Schnelles Profilieren lokaler Daten](/blog/datasummarizer-how-it-works)** Statistik als Schnittstelle Diese beiden Artikel bilden eine kurze Reihe über praktische lokale LLM.

[TOC]

## Der Kern der Erkenntnis

Die Verwendung eines LLM als Datenspeicher ist die falsche Abstraktion..-LLMs sind grundsätzlich nicht in der Lage, Millionen von Zeilen zu scannen, um ein durchschnittliches - zu berechnen, dass ' nicht das ist, was sie für . verwenden. Sogar ein Token-Kontextfenster passt vielleicht ...50,000-Zeilen...

Das richtige Muster **LLM-Gründe: Die Datenbank berechnet die .**

```mermaid
flowchart LR
    A[User Question] --> B[LLM]
    B --> C[SQL Query]
    C --> D[DuckDB]
    D --> E[Results]
    
    style B stroke:#333,stroke-width:4px
    style D stroke:#333,stroke-width:4px
```

Beachten Sie, was geschieht.: Das LLM generiert eine SQL-Abfrage basierend auf Ihrer Frage und das Schema.MS K2 , DuckDB führt es gegen die tatsächlichen Daten aus.. , Die LLM berührt nie Ihre Daten , - , sie sieht nur Spaltennamen und Typen ,. , Dies ist der Grund, warum es schnell , , private , , , und präzise ,.

Für mehr über die Behandlung von Profilen wie das LLM-Interface und ein Beton CLI, das Profil implementiert.- first narration.MSC2 safe SQL- backed QMSC5 registry.- supported sessions and synthetic cloning. **[DatenSummarizer-:-Schnelles Profilieren lokaler Daten](/blog/datasummarizer-how-it-works#the-key-upgrade-statistics-as-the-interface)**.

## Warum laden Sie es nicht einfach in den Speicher?

Die offensichtlichen Ansätze alle teilen die gleichen fatalen Fehler

**[CsvHelper](https://joshclose.github.io/CsvHelper/) Datenrahmen**Lade die gesamte Datei in RAM-.. Ein CSV von 500.MB wird zu einem OOM-Absturz von ..

**SQLite - PostgreSQL**Benötigt einen langsamen Import-Schritt.(Minuten für große Dateien. ),Benötigt Upfront-Schema-Definitionen und Datenbankmanagement Overhead.

**[PandasAI](https://github.com/Sinaptik-AI/pandas-ai)**Immer noch lädt alles ins Gedächtnis..... Plus...,... das Ausführen von LLM, -... generiertem beliebigen Code ist ein Sicherheits-Albtraum.-. SQL ist deklarativ und sandboxfähig.MSC5. Python ist nicht...

## Warum DuckDB

[EnteDB](https://duckdb.org/) Es fragt CSV-Dateien ab *direkt* Kein Importschritt, kein Laden in den Speicher

```csharp
using var connection = new DuckDBConnection("DataSource=:memory:");
connection.Open();
using var cmd = connection.CreateCommand();
cmd.CommandText = "SELECT Region, SUM(Amount) FROM 'sales.csv' GROUP BY Region";
// Executes directly against the file - no import, no memory explosion
```

Das Killer-Feature **es behandelt Dateien als Tabellen**Punktieren Sie es auf eine CSV- oder JSON-Datei und fragen Sie sofort nach..e Kein KREATE-TABLETTE,e kein Masseneinsatz.

Der Faktor |, CsvHelper, SQLite, DuckDB, |,
|--------|-----------|--------|--------|
| **Speicher** Lädt die gesamte Datei ein.Ladungen während des Imports.
| **Einrichtung** Nicht-MSK-Einfuhr
| **500-MB-Datei** Minuten zum Importieren von Sofort-|
| **Datei 5** OOM-Absturz - sehr langsam
| **Parkett** Keine | Kein | Ja schnellere ) Nein |

DuckDB ist, was Daten-Ingenieure in Python für genau diesen Anwendungsfall verwenden [Bindungen an das .-NET](https://github.com/Giorgi/DuckDB.NET) geben Ihnen volle ADO-.-NET Unterstützung Es fühlt sich an wie jede andere Datenbank, außer you.'.re Dateien abfragen

## Der Stapel

|-Komponente - Warum dieser eine Teil?
|-----------|--------------|
| [EnteDB](https://duckdb.org/) Fragen CSV direkt - kein Importschritt
| [EnteDB-.-NET](https://github.com/Giorgi/DuckDB.NET) Volle Unterstützung von ADO-.®NET – ,® fühlt sich einheimisch an
| [Ollama](https://ollama.ai/) Keine API-Tasten keine Cloud
| [Bogus](https://github.com/bchavez/Bogus) Realistische Testdaten in jeder Skala
| [`qwen2.5-coder:7b`](https://ollama.ai/library/qwen2.5-coder) Beste SQL-Genauigkeit bei der Größe 7B

> **Hinweis zur Sicherheit**Dies ist sicherer als beliebiger Code, erfordert aber trotzdem eine Validierung. [Abschnitt Sicherheit](#security-considerations) Für eine breitere Diskussion über das Verwandeln von Profilen in die LLM-Schnittstelle und einen CLI, der diese Muster implementiert, siehe das Begleitstück **[DatenSummarizer-:-Schnelles Profilieren lokaler Daten](/blog/datasummarizer-how-it-works)**.

## Projekt einrichten

Lassen Sie 's ein Beispielprojekt erstellen Installieren Sie die NuGet-Pakete

```bash
dotnet add package DuckDB.NET.Data.Full
dotnet add package OllamaSharp
dotnet add package Bogus
```

Ziehen Sie ein codierendes Modell, das sich auf SQL-: ausrichtet

```bash
ollama pull qwen2.5-coder:7b
```

## Die Architektur

Hier sind die Stücke, wie sie zusammenpassen.

```mermaid
flowchart TB
    subgraph Input
        Q[User Question]
        CSV[CSV File]
    end
    
    subgraph Processing
        Schema[Extract Schema]
        Sample[Get Sample Rows]
        Context[Build LLM Context]
        LLM[Generate SQL]
        Validate[Validate SQL]
        Execute[Execute Query]
    end
    
    subgraph Output
        Results[Query Results]
    end
    
    CSV --> Schema
    CSV --> Sample
    Schema --> Context
    Sample --> Context
    Q --> Context
    Context --> LLM
    LLM --> Validate
    Validate -->|Error| LLM
    Validate -->|OK| Execute
    CSV --> Execute
    Execute --> Results
    
    style LLM stroke:#333,stroke-width:4px
    style Execute stroke:#333,stroke-width:4px
```

Die wichtigsten Erkenntnisse, die wir dem LLM geben **Schema- und Stichprobendaten**Dies hält Kontext klein und antwortet schnell

**Warum das so wichtig ist**Der Validierungsschritt fängt Syntaxfehler vor der Ausführung an Die Wiederholungsschleife behandelt den gelegentlichen Fehler Diese Trennung macht das System sowohl sicher als auch akkurate, für eine detailliertere, ,.CLI--.centered Referenz .(.inklusive Profil ?- first narration and safe SQL execution limits - ) vgl. **[DatenSummarizer-:-Schnelles Profilieren lokaler Daten](/blog/datasummarizer-how-it-works#the-key-upgrade-statistics-as-the-interface)**.

## Schritt Testdaten mit Bogus generieren

> **Sie haben bereits CSV-Daten.?** Überspringen zu [Schritt Erstellen Sie den Schema-Kontext](#step-2-build-the-schema-context).

Bevor wir unseren LLM-Analysator CSV testen können, benötigen wir Daten zum Analysieren von . Für die Entwicklung und das Testen synthetischer Daten schlägt reale Daten:

1. **Skalierungsprüfung** Generieren Sie die Zeilen 100, ,, MKS4 oder 10, um die Leistung in verschiedenen Größen zu überprüfen.
2. **Privatsphäre** Keine Gefahr der Entlarvung von realen Kunden . . / Geschäftsdaten in Demos oder Screenshots
3. **Reproduzierbarkeit** Same seam .= . gleiche Daten machen Bugs reproduzierbar
4. **Kantenfälle** Kontrollieren Sie die Verteilung (...g.,. force liefert die spezifischen Datumsbereiche von ).

### Was ist Bogus-??

[Bogus](https://github.com/bchavez/Bogus) Es erzeugt realistische ,- aussehende gefälschte Daten ,- names , , adressen , , , E-Mails , , , dates , , , Zahlen , mit korrekter Unterstützung für die Lokalisierung , und zwar . , statt manuellen Tests CSV-Dateien oder Verwendung von zufälligen Mülldaten ., , Bogus gibt Ihnen Daten, die *sieht aus* real-:

- `f.Name.FullName()` "John Smith2, nicht "asdf1234")
- `f.Internet.Email()` "johne.hmsmith@gmail.ucom-"uGenau formatiert)
- `f.Date.Between(start, end)` Realistische Datumsverteilung
- `f.Commerce.ProductName()` "Handgefertigte Granit-Käse " ( - , aber wiedererkennbar

Dies spielt eine Rolle, weil realistische Daten Ihnen helfen Probleme zu erkennen, die seltsame Formatierung...... unvorhergesehene Aggregationen.......... Kantenfälle im Datums-Handling...... dass zufällige Strings...

### Definieren Sie das Datenmodell

```csharp
internal class SaleRecord
{
    public string OrderId { get; set; } = "";
    public DateTime OrderDate { get; set; }
    public string CustomerId { get; set; } = "";
    public string CustomerName { get; set; } = "";
    public string Region { get; set; } = "";
    public string Category { get; set; } = "";
    public string ProductName { get; set; } = "";
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal Discount { get; set; }
    public bool IsReturned { get; set; }
}
```

### Den Faker einrichten

Bogus verwendet eine fließende API, um Generierungsregeln zu definieren.

```csharp
var categories = new[] { "Electronics", "Clothing", "Home & Garden", "Sports", "Books" };
var regions = new[] { "North", "South", "East", "West", "Central" };

var faker = new Faker<SaleRecord>()
    .RuleFor(s => s.OrderId, f => f.Random.Guid().ToString()[..8].ToUpper())
    .RuleFor(s => s.OrderDate, f => f.Date.Between(
        new DateTime(2022, 1, 1), 
        new DateTime(2024, 12, 31)))
    .RuleFor(s => s.CustomerId, f => $"CUST-{f.Random.Number(10000, 99999)}")
    .RuleFor(s => s.CustomerName, f => f.Name.FullName())
    .RuleFor(s => s.Region, f => f.PickRandom(regions))
    .RuleFor(s => s.Category, f => f.PickRandom(categories))
    .RuleFor(s => s.ProductName, (f, s) => GenerateProductName(f, s.Category))
    .RuleFor(s => s.Quantity, f => f.Random.Number(1, 20))
    .RuleFor(s => s.UnitPrice, f => f.Random.Decimal(9.99m, 299.99m))
    .RuleFor(s => s.Discount, f => f.Random.Bool(0.3f) ? f.Random.Decimal(0.05m, 0.25m) : 0m)
    .RuleFor(s => s.IsReturned, f => f.Random.Bool(0.05f));
```

Lassen Sie die 's nach unten brechen was geschieht

- **`f` (-Faker, )-Fake** Die Generator-Instanz mit Zugriff auf alle Datenmodule
- **`f.Random.Guid().ToString()[..8]`** Generieren Sie eine GUID, aber nehmen Sie nur erste Zeichen für eine lesbare Bestell-ID.
- **`f.Date.Between()`** Zufälliges Datum innerhalb eines realistischen Bereichs
- **`f.PickRandom(array)`** Wählen Sie zufällig aus vordefinierten Optionen aus.
- **`f.Random.Bool(0.3f)`** Die Wahrscheinlichkeit der wahren (30%' der Aufträge erhalten einen Rabatt.
- **`(f, s)` Syntax** Zugriff sowohl auf gefälschte und die teilweise gebaute Record-. `ProductName` abhängig von `Category`

Das `(f, s)` pattern is performant -- , es bedeutet, dass die erzeugten Daten wesentlich realistischer für das Testen von Aggregationen wie z.B. ," , Umsatz nach Kategorie , ". , sind.

### Generieren und Schreiben an CSV

```csharp
var records = faker.Generate(100_000); // Adjust for your testing needs

await using var writer = new StreamWriter(csvPath, false, Encoding.UTF8);
await writer.WriteLineAsync("OrderId,OrderDate,CustomerId,CustomerName,Region,Category,...");

foreach (var record in records)
{
    var total = record.Quantity * record.UnitPrice * (1 - record.Discount);
    await writer.WriteLineAsync($"{record.OrderId},{record.OrderDate:yyyy-MM-dd},...");
}
```

100K Zeilen erzeugt ca. 15MB von CSV genug zum Testen mit ,, aber Sie können leicht auf Millionen skalieren Die Generation ist schnell (~2. Sekunden für 100.K Reihen ), weil Bogus für Massengeneration optimiert ist

> **Tipp**Einstellen `Randomizer.Seed = new Random(12345)` vor der Generierung reproduzierbare Daten zu erhalten.. , die gleiche Samen , = , gleiche , " ,random ," , Rekorde jedes Mal, wenn es unschätzbar für das Debugging ist.

## Schritt Erstellen Sie den Schema-Kontext

Bevor das LLM SQL®,® erzeugen kann, muss es die Datenstruktur verstehen.

### Das Kontextmodell

```csharp
public class DataContext
{
    public string CsvPath { get; set; } = "";
    public List<ColumnInfo> Columns { get; set; } = new();
    public List<Dictionary<string, string>> SampleRows { get; set; } = new();
    public long RowCount { get; set; }
}

public class ColumnInfo
{
    public string Name { get; set; } = "";
    public string Type { get; set; } = "";  // VARCHAR, DOUBLE, TIMESTAMP, etc.
}
```

Dies erfasst alles, was die LLM benötigt.:-Säulennamen...,-Typen... und ein paar Beispielzeilen, um das Datenformat zu verstehen.

### Extraktion des Schemas

DuckDB kann jeden CSV beschreiben, ohne ihn alle zu laden.

```csharp
private DataContext BuildContext(DuckDBConnection connection, string csvPath)
{
    var context = new DataContext { CsvPath = csvPath };

    // Get schema - DuckDB infers types from the CSV
    using var cmd = connection.CreateCommand();
    cmd.CommandText = $"DESCRIBE SELECT * FROM '{csvPath}'";
    using var reader = cmd.ExecuteReader();

    while (reader.Read())
    {
        context.Columns.Add(new ColumnInfo
        {
            Name = reader.GetString(0),  // Column name
            Type = reader.GetString(1)   // Inferred type
        });
    }

    return context;
}
```

Das `DESCRIBE` Befehl liest nur die Datei-Header plus ein paar Zeilen für den Typ Inference

### Erhalten von Beispieldaten

Beispielreihen helfen dem LLM, Datenformate zu verstehen

```csharp
using var cmd = connection.CreateCommand();
cmd.CommandText = $"SELECT * FROM '{csvPath}' LIMIT 3";
using var reader = cmd.ExecuteReader();

while (reader.Read())
{
    var row = new Dictionary<string, string>();
    for (int i = 0; i < reader.FieldCount; i++)
    {
        var value = reader.IsDBNull(i) ? "NULL" : reader.GetValue(i)?.ToString() ?? "";
        row[reader.GetName(i)] = value;
    }
    context.SampleRows.Add(row);
}
```

Drei Reihen sind in der Regel genug, es zeigt die LLM, welche Formate zu erwarten, ohne Tokens zu verschwenden.

## Schritt SQL mit dem LLM generieren

**Dies ist der harte Teil.** Das Prompt-Engineering hier ist nicht ,-'verhandelbar , ohne strenge Regeln , ,' lokale LLMs werden kreative aber gebrochen SQL , .' Das Ziel ist Determinismus , nicht Kreativität

### Bau der Prompt

```csharp
private string BuildPrompt(DataContext context, string question, string? previousError)
{
    var sb = new StringBuilder();

    sb.AppendLine("You are a SQL expert. Generate a DuckDB SQL query to answer the user's question.");
    sb.AppendLine();
    sb.AppendLine("IMPORTANT RULES:");
    sb.AppendLine("1. The table is accessed directly from the CSV file path");
    sb.AppendLine("2. Use single quotes around the file path in FROM clause");
    sb.AppendLine("3. DuckDB syntax - use LIMIT not TOP, use || for string concat");
    sb.AppendLine("4. Return ONLY the SQL query, no explanation, no markdown");
    sb.AppendLine();
    
    sb.AppendLine($"CSV File: '{context.CsvPath}'");
    sb.AppendLine($"Row Count: {context.RowCount:N0}");
    sb.AppendLine();
    
    // Schema
    sb.AppendLine("Schema:");
    foreach (var col in context.Columns)
    {
        sb.AppendLine($"  - {col.Name}: {col.Type}");
    }
```

Der Abschnitt Regeln ist entscheidend -- , es sagt dem LLM genau, wie man die Abfrage für DuckDB , . , Da explizit über Syntax , ( , LIMIT vs TOP ,, , String-Verkettung , ) , verhindert häufige Fehler .

### Hinzufügen von Beispieldaten

```csharp
    if (context.SampleRows.Count > 0)
    {
        sb.AppendLine();
        sb.AppendLine("Sample data (first 3 rows):");
        foreach (var row in context.SampleRows)
        {
            var values = row.Select(kv => $"{kv.Key}='{kv.Value}'");
            sb.AppendLine($"  {{{string.Join(", ", values)}}}");
        }
    }
```

### Fehlerwiederherstellung

Wenn ein früherer Versuch fehlgeschlagen ist, schließen Sie den Fehler ein.

```csharp
    if (previousError != null)
    {
        sb.AppendLine();
        sb.AppendLine("YOUR PREVIOUS QUERY HAD AN ERROR:");
        sb.AppendLine(previousError);
        sb.AppendLine("Please fix the query based on this error.");
    }

    sb.AppendLine();
    sb.AppendLine($"Question: {question}");
    sb.AppendLine();
    sb.AppendLine("SQL Query (no markdown, no explanation):");

    return sb.ToString();
}
```

Dieser Retry-Mechanismus ist wichtig. lokale LLMs machen manchmal Syntaxfehler.

### Anrufen der LLM

```csharp
var request = new GenerateRequest { Model = _model, Prompt = prompt };
var response = await _ollama.GenerateAsync(request).StreamToEndAsync();
var sql = CleanSqlResponse(response?.Response ?? "");
```

Das `StreamToEndAsync()` wartet auf die komplette Antwort für ein besseres UX-, streamen Sie Token, wie sie ankommen

### Reinigung der Reaktion

LLMs oft wickeln SQL in Markdown Code-Blöcke, obwohl nicht gesagt wird, um

```csharp
private string CleanSqlResponse(string response)
{
    var sql = response.Trim();

    // Remove markdown code blocks if present
    if (sql.StartsWith("```"))
    {
        var lines = sql.Split('\n').ToList();
        lines.RemoveAt(0);  // Remove opening ```sql
        if (lines.Count > 0 && lines[^1].Trim().StartsWith("```"))
        {
            lines.RemoveAt(lines.Count - 1);  // Remove closing ```
        }
        sql = string.Join('\n', lines);
    }

    return sql.Trim('`', ' ', '\n', '\r');
}
```

## Schritt vor der Ausführung bestätigen

EnteDB-' `EXPLAIN` lässt uns SQL-Syntax überprüfen, ohne die Abfrage zu starten.

```csharp
private string? ValidateSql(DuckDBConnection connection, string sql)
{
    try
    {
        using var cmd = connection.CreateCommand();
        cmd.CommandText = $"EXPLAIN {sql}";
        cmd.ExecuteNonQuery();
        return null; // Valid
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
}
```

Wenn die Validierung fehlschlägt, füttern wir den Fehler zurück an das LLM und versuchen es erneut bis zu einem Limit.

## Schritt Ergebnisse ausführen und formatieren

Schließlich führen Sie die Abfrage aus und formatieren die Ausgabe.

```csharp
private QueryResult ExecuteQuery(DuckDBConnection connection, string sql)
{
    var result = new QueryResult { Sql = sql };

    try
    {
        using var cmd = connection.CreateCommand();
        cmd.CommandText = sql;
        using var reader = cmd.ExecuteReader();

        // Capture column names
        for (int i = 0; i < reader.FieldCount; i++)
        {
            result.Columns.Add(reader.GetName(i));
        }

        // Capture rows
        while (reader.Read())
        {
            var row = new List<object?>();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                row.Add(reader.IsDBNull(i) ? null : reader.GetValue(i));
            }
            result.Rows.Add(row);
        }

        result.Success = true;
    }
    catch (Exception ex)
    {
        result.Success = false;
        result.Error = ex.Message;
    }

    return result;
}
```

Das `QueryResult` class Im Beispielprojekt zeigt sich die Klasse (. `ToString()` Methode, die Formate als lesbare Tabelle ergibt

## Konversationskontext hinzufügen

Für die interaktive Analyse: ,-Anwender wollen häufig folgende Fragen stellen:

```
"What's the total revenue?"
→ "Break that down by region"
→ "Show the top 5 regions"
```

Die zweite und dritte Frage ergibt nur Sinn mit Kontext aus dem ersten

### Gesprächsgeschichte verfolgen

```csharp
public class ConversationTurn
{
    public string Question { get; set; } = "";
    public string Sql { get; set; } = "";
    public bool Success { get; set; }
    public int RowCount { get; set; }
    public string Summary { get; set; } = "";  // "Single value: 1234567.89"
}
```

### Einschließlich Geschichte in Prompts

```csharp
if (_history.Count > 0)
{
    sb.AppendLine();
    sb.AppendLine("CONVERSATION HISTORY (for context):");
    
    foreach (var turn in _history.TakeLast(5))  // Last 5 turns
    {
        sb.AppendLine($"Q: {turn.Question}");
        sb.AppendLine($"SQL: {turn.Sql}");
        if (turn.Success)
        {
            sb.AppendLine($"Result: {turn.Summary}");
        }
        sb.AppendLine();
    }
}
```

Die Geschichte gibt dem LLM-Kontext, um Referenzen zu verstehen, wie z.B. ",, die Ergebnisse von ", oder ", brechen sie weiter nach unten

## Welches Modell zu verwenden?

Für die SQL-Generation – ,-codierung – --fokussierte Modelle funktionieren am besten –.- Hier finden Sie die verfügbaren Optionen über [Modellbibliothek von Ollama-'](https://ollama.ai/library):

|-Modell Größe |-Geschwindigkeit Qualität |-Verknüpfung
|-------|------|-------|---------|------|
| `qwen2.5-coder:7b` Schnelles, schnelles und ausgezeichnetes | [Ollama](https://ollama.ai/library/qwen2.5-coder) |
| `deepseek-coder-v2:16b` |, das beste Mittel der |, [Ollama](https://ollama.ai/library/deepseek-coder-v2) |
| `codellama:7b` ,| , , 4 , GB , | , schnell , | , gut ,| , [Ollama](https://ollama.ai/library/codellama) |
| `llama3.2:3b` Sehr schnell, sehr schnell. [Ollama](https://ollama.ai/library/llama3.2) |

Für die meisten Anwendungsfälle **`qwen2.5-coder:7b`** trifft den süßen Punkt, der auf bescheidener Hardware läuft.

## Leistung

### Reale Benchmarks der Welt

Testen auf einer Zeile CSV-Datei (14, MB, ), auf einem Standarddev-Gerät (, Ryzen, 5,, NVMe SSD,, MSC6, GB RAM,):,

Frage nach dem Typ der Zeit
|------------|------|
Einfaches Land ,| , 65 ,|
GROUP BY MIT SUM |58-68ms »|«
Komplexe Aggregation mit FILTER
Multi---Table GROUP BY mit HAVING-MSC2, 71, |,

Sub--100-ms für analytische Abfragen auf den Zeilen 100-K ohne Importschritt .-Queren von Komplexitätsfragen mehr als Zeilenanzahl ;-Duck DBK5-Säulenmotor verarbeitet Aggregationen effizient unabhängig von der Dateigröße.-Segment

### Große Dateien in Parkett konvertieren

Für Dateien über 1, GB, ,, [Parkettformat](https://parquet.apache.org/) ist schneller als :

```csharp
using var cmd = connection.CreateCommand();
cmd.CommandText = $"COPY (SELECT * FROM '{csvPath}') TO '{parquetPath}' (FORMAT PARQUET)";
cmd.ExecuteNonQuery();
```

Die komprimierte Parkett-Datei ist auch viel kleiner.

### Sicherheitsaspekte

Beim Ausführen von LLM---generierter SQL-:

```csharp
private bool IsSafeQuery(string sql)
{
    var dangerous = new[] { "DROP", "DELETE", "TRUNCATE", "UPDATE", "INSERT", "ALTER", "CREATE" };
    var upperSql = sql.ToUpperInvariant();
    return !dangerous.Any(d => upperSql.Contains(d));
}
```

DuckDB-'s im --Memory-Modus bietet auch natürliche Isolation, die Ihre Produktionsdatenbanken beeinflussen kann.

## Vollständiges Beispiel

Hier ist ', wie alles zusammenkommt

```csharp
// Generate test data
await GenerateSalesCsvAsync("sales.csv", 100_000);

// Simple query
using var service = new CsvQueryService("qwen2.5-coder:7b", verbose: true);
var result = await service.QueryAsync("sales.csv", "What are total sales by region?");
Console.WriteLine(result);

// Conversational analysis
using var analyser = new ConversationalCsvAnalyser("sales.csv", "qwen2.5-coder:7b");

Console.WriteLine(await analyser.AskAsync("What's the total revenue?"));
Console.WriteLine(await analyser.AskAsync("Break that down by category"));
Console.WriteLine(await analyser.AskAsync("Which category has the most returns?"));
```

## Zusammenfassung

Das mentale Modell, um zu halten **LLMs Grund ,; , Datenbanken berechnen , . ,**

Don.'.t feed data to the LLM.MSC1. Feed it schema.,. let it generate SQL?,. execute that SQL any prospective engine.MS K4. Diese Trennung ist, warum der Ansatz im Maßstab funktioniert.

Die Implementierung

1. **EnteDB** Abfragen von CSV direkt - keine Importe von ,
2. **Schema Proben** dem LLM genügend Kontext zu geben, ohne Daten freizulegen
3. **Strenge Sofortregeln** force deterministische SQL., nicht kreative Prosa
4. **Validierung über EXPLAIN** Fangfehler vor der Ausführung
5. **Retry mit Fehler-Feedback** behandelt den gelegentlichen Syntax-Slip

Das Ergebnis: Sub--100ms analytische Abfragen auf Millionen -row-Dateien, komplett offline , mit Daten, die nie Ihre Maschine verlassen

Das vollständige Beispielprojekt ist verfügbar unter: [Meistlylucid-.-CsvLm](https://github.com/scottgal/mostlylucidweb) „-" schließt ein `CsvQueryService`, `ConversationalCsvAnalyser`Datengenerierung auf Basis von , und Bogus--

## Aufkommen

### EnteDB

- [DuckDB-Dokumentation](https://duckdb.org/docs/) Vollständiger Verweis
- [DuckDB CSV importieren](https://duckdb.org/docs/data/csv/overview.html) Besondere Merkmale von CSV--
- [DuckDB SQL Referenz](https://duckdb.org/docs/sql/introduction) SQL-Syntaxunterschiede von anderen Datenbanken
- [EnteDB-.-NET GitHub](https://github.com/Giorgi/DuckDB.NET) Bindungen
- [EnteDB-.-NET NuGet](https://www.nuget.org/packages/DuckDB.NET.Data.Full) Vollständiges Paket mit nativen Binärdateien

### LLMs von Ollama

- [Ollama](https://ollama.ai/) Lokale LLM-Laufzeit
- [Ollama Model Library](https://ollama.ai/library) Verfügbare Modelle
- [OllamaSharp](https://github.com/awaescher/OllamaSharp) Client-Bibliothek
- [OllamaSharp NuGet](https://www.nuget.org/packages/OllamaSharp/)

### Generierung von Testdaten

- [Bogus GitHub](https://github.com/bchavez/Bogus) Gefälschte Daten-Generator
- [Bogus API-Referenz](https://github.com/bchavez/Bogus#bogus-api-support) Verfügbare Datentypen

### Alternativen genannt

- [CsvHelper](https://joshclose.github.io/CsvHelper/) CSV Parsing-Bibliothek
- [Microsoft-.-Datenanalyse](https://www.nuget.org/packages/Microsoft.Data.Analysis) DataFrame für .-NET
- [PandasAI](https://github.com/Sinaptik-AI/pandas-ai) Integration von Python LLM-Pandas

### Verwandte Artikel

- [DatenSummarizer-:-Schnelles Profilieren lokaler Daten](/blog/datasummarizer-how-it-works) Artikel über CLI-- auf Basis von Datenprofilen