One Entity, One Row: When Multiple pgvector Columns Beat Row-per-Chunk Storage
When developers begin storing embeddings in PostgreSQL with pgvector, one design often appears almost automatically:
One Entity, One Row: When Multiple pgvector Columns Beat Row-per-Chunk Storage
When developers begin storing embeddings in PostgreSQL with pgvector, one design often appears almost automatically:
One embedding = one row.
That pattern makes perfect sense for classic document RAG, where a long article, PDF, or transcript is split into many chunks and each chunk must be independently searchable.
But what if the data is not a long document?
What if each record represents a structured entity with a small, fixed number of meaningful text fields — for example:
titlesummarydescription
or:
short_textdetailed_textmetadata_explanation
Should those embeddings always become separate rows?
Not necessarily.
In many entity-centric retrieval systems, it can be cleaner — and sometimes more storage-efficient — to store one entity per row with multiple vector columns, rather than duplicating the same entity across multiple rows.
The Two Schema Designs
Assume each entity has three fields that we want to embed:
- Title
- Summary
- Description
Option 1: Row-per-field / row-per-chunk model
Each embedding becomes a separate row.

Row-per-field table
Visual model
Entity 101
├── Row 1 → title embedding
├── Row 2 → summary embedding
└── Row 3 → description embedding
Option 2: One row per entity with multiple vector columns
Each entity remains a single row.
entity_idtitle_embeddingsummary_embeddingdescription_embedding101vector_1vector_2vector_3
Visual model
Entity 101
└── Single Row
├── title_embedding
├── summary_embedding
└── description_embedding
The Core Question: Are You Retrieving Chunks or Entities?
This is the key design question.
If your goal is:
“Find the most relevant individual text passage,”
then the row-per-chunk model is often the right choice.
But if your goal is:
“Find the most relevant entity by comparing several known semantic fields,”
then the one-row, multi-vector-column model can be a better fit.
That distinction changes the schema.
Why Multiple Vector Columns Can Be Better
1. The database shape matches the business object
If the actual object is an entity, then storing it as one row keeps the data model natural.
In the multi-row design, the same entity is split across multiple records:
101, title
101, summary
101, description
That means retrieval first returns field-level rows, and only afterward do you reconstruct the entity.
In the multi-column design, retrieval remains entity-first. The row itself is already the object you care about.
2. It reduces repeated metadata and row overhead
This is an overlooked cost.
Suppose every entity carries metadata such as:
entity_idsource_idcategorycreated_atupdated_at- status flags
- foreign keys
In a row-per-field design, that metadata is repeated again and again for every embedding row.
Row-per-field storage pattern
Row 1: entity_id + metadata + title_embedding
Row 2: entity_id + metadata + summary_embedding
Row 3: entity_id + metadata + description_embedding
Multi-column storage pattern
One row: entity_id + metadata + title_embedding + summary_embedding + description_embedding
The vector data itself is still present in both designs. But the row-per-field model also pays for:
- repeated IDs
- repeated chunk-type labels
- repeated metadata values
- additional row headers
- more heap tuples
- potentially larger indexes on repeated metadata columns
The more metadata you duplicate, the more noticeable this becomes.
3. Storage can grow faster with separate rows
Let’s use a simplified example.
Imagine:
- 1 million entities
- 3 embeddings per entity
- each embedding is 1,536 dimensions
pgvector states that a standard vector uses:
4 × dimensions + 8 bytes
So a 1,536-dimensional vector is roughly:
4 × 1536 + 8 = 6152 bytes ≈ 6 KB
That means the raw embedding payload is roughly:
1 million entities × 3 vectors × ~6 KB ≈ 18 GB
That 18 GB exists in either design because the same three vectors are being stored.
But now consider the extra relational overhead.
Row-per-field design
3 million rows
Multi-column design
1 million rows
The row-per-field model stores the same vectors, but it also requires roughly three times as many table rows. That can increase:
- tuple/header overhead
- repeated keys
- repeated field-type values
- duplicated non-vector attributes
- index entries for columns like
entity_id,field_type,category, or timestamps
The exact storage difference depends on the schema, data types, indexes, and PostgreSQL storage behavior. But structurally, the row-per-field approach often carries more relational overhead when the entity fields are fixed and known in advance.
4. It simplifies entity-level retrieval
Consider a search query that should compare against several fields.
With multiple vector columns, you can compute entity-level relevance directly:
SELECT
entity_id,
0.5 * (1 - (title_embedding <=> :query_vector)) +
0.3 * (1 - (summary_embedding <=> :query_vector)) +
0.2 * (1 - (description_embedding <=> :query_vector)) AS score
FROM entities
ORDER BY score DESC
LIMIT 10;
This expresses the business logic clearly:
- title matters most
- summary matters next
- description adds supporting context
With row-per-field storage, you often need to:
- search rows
- group by entity
- aggregate scores
- deduplicate entities
- handle cases where multiple fields from the same entity dominate the result set
That is doable — but more complex.
5. It makes field-aware weighting natural
Not every field should influence ranking equally.
A query match in a title might matter more than a weak match in a long description.
With separate vector columns, weighting is explicit:
Final score =
0.5 × title similarity
+ 0.3 × summary similarity
+ 0.2 × description similarity
Diagram: entity-level weighted scoring
Query Vector
│
┌──────────────┼──────────────┐
│ │ │
title vector summary vector description vector
weight 0.5 weight 0.3 weight 0.2
│ │ │
└──────────────┴──────────────┘
│
Final Entity Score
This makes the retrieval strategy easier to understand, test, and evolve.
But This Is Not Always Better
The one-row, multiple-vector-column design is not a universal replacement for chunk tables.
It works best when:
- the number of vectorized fields is small
- the fields are known ahead of time
- each field has a distinct semantic role
- retrieval should return whole entities
- field-specific scoring matters
It is a poor fit when:
- the number of chunks is variable
- an entity may have 5 chunks today and 500 tomorrow
- you are indexing long documents or transcripts
- the best result should be a specific paragraph, not the full entity
- the system needs flexible chunk creation and deletion
When Row-per-Chunk Is Still the Right Model
For document retrieval, row-per-chunk remains the natural design.
Example

This is ideal when:
- documents are long
- chunk counts vary widely
- passage-level recall matters
- you want the best matching text fragment
The design problem is not:
“Which schema is universally better?”
It is:
“Which schema matches the retrieval unit?”
pgvector-Specific Tradeoff: More Columns Can Mean More Indexes
There is a real cost to the multi-column design.
If you want approximate nearest-neighbor search on several vector columns, you may need separate indexes such as:
CREATE INDEX ON entities USING hnsw (title_embedding vector_cosine_ops);
CREATE INDEX ON entities USING hnsw (summary_embedding vector_cosine_ops);
CREATE INDEX ON entities USING hnsw (description_embedding vector_cosine_ops);
That means:
- more indexes to maintain
- more index storage
- more write cost during inserts and updates
So the multi-column model should not be chosen blindly. It is attractive when the schema is stable and the semantic fields are genuinely important enough to search independently or combine in scoring.
PostgreSQL Storage Note: Wide Rows Are Not Free Either
A row containing several vectors can become large. PostgreSQL uses TOAST storage to handle oversized values transparently when a row cannot fit comfortably on standard pages.
That means the multi-column model also has a cost:
- wider tuples
- possible out-of-line storage behavior
- tradeoffs in locality and row access patterns
So the goal is not to pretend that one row with many vectors is “free.”
The point is more specific:
If the number of vectorized fields is small and fixed, keeping them on one entity row can avoid the repeated relational overhead of exploding the same entity into several rows.
Final Takeaway
The common instinct is:
“I have several embeddings, so I need several rows.”
But that is not always the best design.
If your system deals with structured entities that have a small, fixed set of semantically meaningful text fields, then storing those embeddings as multiple pgvector columns in one row can be a better fit than storing one row per field.
It can provide:
- a cleaner entity-centered schema
- less repeated metadata
- fewer total rows
- simpler entity-level ranking
- natural weighted scoring across fields
- less deduplication after retrieval
At the same time, row-per-chunk storage remains the right choice for long documents, variable chunks, and passage-level retrieval.
The better schema is the one that matches what you are actually trying to retrieve:
Chunks? Use rows. Structured entities with fixed semantic fields? Consider columns.
메타데이터
- post_id
- 0887d8b0cd5e
- slug
- one-entity-one-row-when-multiple-pgvector-columns-beat-row-per-chunk-storage-0887d8b0cd5e
- url
- https://medium.com/@maniallada28/one-entity-one-row-when-multiple-pgvector-columns-beat-row-per-chunk-storage-0887d8b0cd5e
- canonical_url
- https://medium.com/@maniallada28/one-entity-one-row-when-multiple-pgvector-columns-beat-row-per-chunk-storage-0887d8b0cd5e
- author_url
- https://medium.com/@maniallada28
- status
- ok
- fetched_at
- 2026-06-09 15:37:30