Skip to content
RAG Explained Better

Handling Tables and Structured Data in RAG

Why a flattened table retrieves badly and answers worse, and the extraction strategies that keep numbers intact.

Table ingestion for RAG extracts rows, columns, headers, and continuity signals as a structured artifact — not as a left-to-right string — and then serializes labelled units for retrieval. Flattening tables breaks the relationship between a number and the header that gives it meaning (Yu et al., 2025 TableRAG). This page owns extract + serialize + merge; if your table unit is clean but the answer still looks wrong, diagnosis routes to /failures/tables and the next step after structured units exists on /chunking/tables.

Why this matters: table facts are relational, so “wrong extraction” behaves like a wrong retrieval target — the retriever may still find the right document, but it cannot find the right cell context.

Why does flattening a table break RAG?

Flattening a table breaks RAG because it severs header↔cell and row↔entity links. Retrieval can then return digit strings without the structured context that tells the model what each number means.

Ailog (2025) shows the same failure mode with a concrete example: a Product/Price/Stock table becomes “Product Price Stock Widget A $99 In stock …” after naive parsing, losing the original cell relationships. TableRAG (Yu et al., 2025) frames this as “flattening and chunking disrupt the intrinsic tabular structure,” undermining heterogeneous reasoning over tables.

  • Header loss. Values arrive without the column label.
  • Row identity loss. Row entities get mixed or dropped.
  • Unit and scale loss. Magnitude notes and legends disappear with the structure.
  • Continuation loss. Multi-page tables may lose where the next page continues.

How do you extract tables from a PDF for RAG?

Extract tables from a PDF for RAG by using a document-type parser gate, not a single “best library”: bordered native PDFs route to camelot’s lattice mode; borderless or whitespace-aligned tables route to stream-style extraction or pdfplumber; scanned pages route through OCR-first structure; and multimodal vision models are a fallback when parsers cannot recover merged structure.

  1. Bordered tables. Use a lattice-style approach like camelot lattice for ruled grids.
  2. Borderless tables. Use stream/whitespace alignment (or pdfplumber extract_tables) for borderless layouts.
  3. Complex layout and tables-as-figures. Use hi-res/document OCR with table-structure inference when the table is not easily recoverable as a text grid.
  4. Scanned pages. If there is no usable text layer, route to the OCR path first (see /ingestion/ocr) and then recover structure.
  5. Vision fallback. Markai reports that multimodal table reading can cost and latency about ~10× higher per page than text extraction, so treat vision as a fallback, not a default (Markai blog, 2026 capture).

Regardless of the parser, the output you want is structured rows with headers (and merged-cell continuity when present), ready to be serialized next.

How do you extract tables from Excel for RAG?

Extract tables from Excel for RAG by preserving structure and real cell values: use openpyxl with data_only=True so you read computed values instead of formulas, and prefer named Excel tables over whole-sheet dumps so your serialization has stable labels.

When you serialize, attach at least the sheet name and table name as metadata so table retrieval can answer questions scoped to the right worksheet.

How should you serialize a table before chunking?

Serialize a table before chunking so each retrievable unit is a self-contained labelled record — source + table label + headers + rows — and never bare digits. This ensures the chunk carries the information needed for cell-level retrieval and digit validation.

  • Markdown or grid serialization. Keep a table-shaped block when the rows remain coherent.
  • Header:value record serialization. Convert each row (or row-group) into labelled statements the retriever can match.
  • Optional LLM rewrite. Use an LLM natural-language rewrite only when it helps embeddings, but keep a structured representation for exact-digit citation and validation.

Once serialized, hand the structured units to /chunking/tables for the row-aware splitting strategy.

How do you handle multi-page and merged-cell tables?

Handle multi-page and merged-cell tables by merging before chunking: page-wise extracts are not logical tables, so continuations and merged labels must be carried forward until the table becomes one coherent unit.

Somtheegala (2025) describes continuation as a geometry-and-alignment problem, with the strongest signal coming from horizontal column alignment when the table spans pages. Optyx-style pipelines emphasize carry-forward of merged labels and link footnotes to rows so that a row fragment on page 2 still has the correct header context.

Operational checks: ensure you emit one table_id spanning pages and that you do not embed page-2 orphan rows without headers.

When do you keep both a text view and a structured table?

Keep both a text view and a structured table when you need both similarity search and exact cell-level citation: embed a normalized text/markdown (or table-summary) view, but preserve the structured table artifact for verification and digit-level correctness.

LangChain multi-vector patterns reflect this: retrieve on a summary or text representation, then let the generator read the raw table on hit. This dual artifact approach reduces the risk that the model “reconstructs” a value that was never actually present in the table.

SQL-based heterogeneous frameworks (like TableRAG) can be another path when your corpus is already relational, but that architecture depth is not this page’s mechanism boundary.

What failure does bad table ingestion cause?

Bad table ingestion causes wrong digits, missing headers, orphaned continuation rows, or unit/scale drops — often while the right document is retrieved. The symptom belongs to /failures/tables. After your structured extraction is correct, unit-of-retrieval mistakes move to /chunking/tables.

What is table ingestion in RAG?

Table ingestion in RAG is the ingest-time mechanism that extracts tables as structured rows and cells (including headers, merged-cell continuity, and units) and then serializes labelled table units for retrieval. Flattening tables into a plain left-to-right string breaks the cell meaning that retrieval needs (Yu et al., 2025 TableRAG; Ailog, 2025).

camelot lattice vs stream?

Camelot lattice is meant for bordered PDFs with visible grid lines, where lattice mode can detect the table geometry. Camelot stream (and pdfplumber extract_tables) is meant for borderless or whitespace-aligned tables, where columns align via whitespace patterns rather than explicit rules.

Does camelot work on scans?

Camelot-style parsing works best when the PDF content has the right structure for table detection. For scanned PDFs without a usable text layer, you generally route to OCR-first structure recovery (see /ingestion/ocr) and only then apply table extraction/serialization, because naive text extraction destroys the table grid context.

Excel formulas vs values?

For RAG ingestion, you usually want Excel computed values, not formulas. Markai’s table ingestion approach uses openpyxl with data_only=True so that you embed the values the sheet would display.

Serialize before or after chunking?

Serialize before chunking. Serialization creates self-contained labelled records (headers + row values or row-groups) so the chunker splits complete facts instead of slicing digits away from their meaning. The row-aware splitting strategy then lives on /chunking/tables.

How is this different from /chunking/tables and /failures/tables?

This page is the ingestion mechanism: detect the table type, extract it structurally, merge multi-page continuity, and serialize labelled units. /chunking/tables is the splitting strategy after you already have structured units, and /failures/tables is the diagnostic layer for symptoms like wrong digits or missing headers.