Retrieval
Do I need a vector database if I use Postgres?
You need a separate vector database only if nearest-neighbor search cannot live in Postgres. pgvector stores vectors on the same row and answers similarity in SQL. A dedicated engine is for filtered ANN at scale, or for blobs SQL was not built to serve. Many apps stay on Postgres plus an embed job they already run.
Updated · Part of How does an embedding index differ from a vector database?
Short answer
Usually no. If Postgres already holds the application row, pgvector adds a vector column, exact search, and HNSW or IVFFlat indexes so nearest neighbors are a SQL query. You still compute embeddings in a worker you write, and media bytes usually live in object storage. A second database is optional until that shape breaks.
What pgvector actually is
pgvector is open-source vector similarity search for Postgres: a vector type, L2 / inner product / cosine distance, and optional HNSW or IVFFlat indexes. Filters and joins use the rest of the SQL row. Any host that runs the extension (self-managed Postgres, Supabase, Neon, and others) can serve it. It is not a multimodal lake and not a dedicated ANN cluster. Repo: https://github.com/pgvector/pgvector
When a dedicated vector database still wins
A dedicated engine is the better fit when the serving contract is filtered ANN as a service, not SQL next to the row.
- Qdrant when payload indexes on HNSW, dense and sparse vectors, and a Rust engine you can shard are the product. Docs: https://qdrant.tech/documentation/overview/
- LanceDB when images, video, audio, text, and embeddings should share one Lance table, embedded or as a lakehouse. Docs: https://docs.lancedb.com/
- Pinecone, Weaviate, or Milvus when that vendor query API, hybrid BM25 fusion, or a scale-out cluster is already the contract.
- Stay on pgvector when the vector count and QPS fit one Postgres instance and SQL joins are how you filter.
How to choose
Start from the system of record, not from a latency bake-off this page did not run.
- If the source of truth is a Postgres row, try pgvector first.
- If the source of truth is a Lance table or a dedicated ANN service, pick that engine and keep Postgres for the parts that are SQL.
- If the source of truth is the passage, frame, or file, declare the index on that table so insert keeps it current. That is not pgvector and not a second database.
Last reviewed
Facts on this page were checked against vendor documentation on 2026-10-28. Product surfaces move; treat version names and APIs as directional and follow the official links in Related.
Postgres versus a vector database (last reviewed: Oct 2026)
| Approach | Where vectors live | Filters | When it is enough |
|---|---|---|---|
| Postgres only | Nowhere; you have no ANN | SQL on scalars | No similarity search needed |
| Postgres plus pgvector | A vector column on the same row | SQL WHERE plus distance | The row is already in Postgres and one instance holds the vectors |
| Dedicated vector DB (Qdrant, Pinecone, Weaviate, Milvus) | Points or objects in that engine | Payload or metadata filters on the ANN graph | Filtered ANN, sharding, or a vendor query SLA is the contract |
| LanceDB | The same Lance table as blobs and features | SQL-style filters, FTS, hybrid (vendor docs) | Multimodal lake layout is the product |
| Index on the source table | On the passage, frame, or file row | where() on that table plus similarity() | Insert should embed and the media is typed columns, not a SQL row you already have |
Where Pixeltable fits
Use Pixeltable when the system of record is the passage, frame, or file, not a Postgres row you already join: declare pxt.EmbeddingIndex, query with column.similarity(string=...), and insert keeps the index current. Pixeltable is not pgvector and not a drop-in Qdrant cluster. Keep pgvector when Postgres already holds the row and you will embed in a worker you write. Keep Qdrant, Pinecone, Weaviate, or Milvus when that engine is the serving contract. Keep LanceDB when Lance layout is the product. A product list is pgvector vs Qdrant vs LanceDB.
import pixeltable as pxtfrom pixeltable.functions.huggingface import sentence_transformerTableModel = pxt.model_base()embed = sentence_transformer.using(model_id='sentence-transformers/all-MiniLM-L6-v2')class Passages(TableModel, name='passages'):text: pxt.Stringsource: pxt.String__indexes__ = [pxt.EmbeddingIndex(text, string_embed=embed),]@pxt.querydef search_passages(query: str, limit: int = 5):sim = Passages.text.similarity(string=query)return (Passages.order_by(sim, asc=False).limit(limit).select(Passages.text, Passages.source, sim))
Questions
Is pgvector a vector database?
When is pgvector not enough?
Does Postgres store the video files?
Do I still have to embed myself?
Next step
Install the Pixeltable skill, or sign up for Cloud and run the same file on a hosted database.