How a graph database works vs joins
TL;DR - In a relational database a relationship is a value in a join table: every hop is re-derived at query time by matching keys. In a graph database a relationship is stored structure: every hop is a pointer you follow. That one difference is why multi-hop questions - who’s connected to whom, through what, within how many steps - stay fast on a graph as data grows, and why they get combinatorially painful as SQL joins. Set-shaped work - aggregations, scans, reporting - still belongs in SQL.
Connected data has a shape
A social network, a payment flow, a referral chain, a supply chain, an agent’s memory of which tools touched which records - these aren’t tables that happen to reference each other. They’re networks: things (nodes) connected by typed, directional relationships (edges), both carrying properties.
The graph data model stores exactly that:
- Nodes - entities:
(patient),(provider),(account),(device). - Edges - typed, directed relationships:
(account)-[:PAID]->(merchant),(doctor)-[:REFERRED]->(specialist). Edges are first-class: they have their own properties (amount, timestamp, weight).
Nothing exotic so far - you can model this relationally. The difference is what the database does with it.
How relational handles relationships
SQL represents a many-to-many relationship as a join table: referrals(from_id, to_id, date). The relationship exists as rows, and every query that uses it must re-derive the connection by matching keys - that’s what a join is.
One hop, fine. But connected questions are rarely one hop:
“Which providers are within three referrals of this one?”
Relationally, that’s the join table joined to itself three times. With a good index each hop is a per-key B-tree probe - the relationship is still re-derived at query time, at log cost per edge, and on wide frontiers the planner may fall back to whole-table hash joins. The sharper pain is structural: intermediate result sets fan out combinatorially before being filtered back down; “within one to five referrals” means generating SQL per depth or writing a recursive CTE the planner treats as a black box; and every added hop compounds all of it.
What a graph database does differently
A graph store makes adjacency a storage primitive. Each node knows its edges directly - conceptually, following an edge is a lookup keyed by the node, not a scan-and-match over a global table. The property this buys is the whole point:
Traversal cost is proportional to the part of the graph you actually touch, not to the total size of the database.
“Friends of friends of Alice” visits Alice, her ~50 edges, and their ~2,500 edges - the same work whether the database holds a thousand users or a hundred million. In the join world that same question keeps getting slower as tables grow, because every hop re-derives membership against ever-bigger structures.
Traversals compose into the operations connected questions are made of: neighbors, shortest paths, variable-depth expansion (1..5 hops), and pattern matching - “find this shape anywhere in the graph.”
Cypher in sixty seconds
Graph queries read like the sentence you’d say out loud. Cypher, the most widely adopted syntax, draws the pattern:
// Who did Dr. Rao refer patients to, directly or through one intermediary?
MATCH (d:provider {name: "Dr. Rao"})-[:REFERRED*1..2]->(p:provider)
RETURN DISTINCT p.name
// Fraud shape: does money leaving this account cycle back to it within 4 hops?
MATCH path = (a:account {id: $suspect})-[:PAID*2..4]->(a)
RETURN path
The second query is the famous one. Finding cycles in SQL means self-joining a payments table up to four times and comparing endpoints - a query you write once, hate forever, and watch degrade as the table grows. In Cypher it’s one line, and the exploration stays bounded to the suspect’s own up-to-4-hop neighborhood - no global join materialization.
The advantages, in one place
- Multi-hop cost tracks the answer, not the dataset. You pay for the neighborhood you explore. This is the structural advantage; the rest follow from it.
- Queries read like the question. Less translation between product thinking and query text means fewer wrong queries.
- Variable and unknown depth are native. “Within N hops” and “shortest path” are expressions, not generated SQL.
- Relationship-first evolution. New edge types are additive. The moment you need
(:device)-[:SHARED_BY]->(:account)for fraud detection, you write edges - no join-table migration, no ORM churn. - Pattern detection as a first-class operation. Rings, chains, hubs, communities - shapes that signal fraud, influence, or risk - are match patterns, not analytics jobs.
Where SQL still wins
Graphs are not a better everything. Aggregations and scans - “revenue by region last quarter” - are set operations; SQL’s planner, indexes, and window functions are built for them and win. Constraint-heavy transactional flows lean relational. Nobody wants tax reporting as a traversal.
The uncomfortable truth is that real applications need both shapes on the same data: the payment row that lands in your revenue report is the same payment edge the fraud query walks. Historically that meant running a relational database and a graph database, plus the ETL to keep two copies of the truth in sync - and that pipeline, not either database, becomes the thing that pages you.
Graph on OriginChainDB
Our answer is one store that speaks both: rows you query with SQL are the same records you traverse with Cypher - one write, no sync pipeline, no second system to operate. The referral queries above run against tables you can also GROUP BY; an agent’s tool-call history is rows for auditing and a graph for “what touched this record before it broke.” Vector similarity composes with both, because recommendation and memory questions are usually “nearest neighbors, then walk the graph from there.”
If you’re weighing a dedicated graph database, the real question isn’t “graph or relational” - it’s whether the connected questions justify operating and synchronizing a second store. When the graph is another query shape over data you already have, the answer changes.
FAQ
Is a graph database just an ORM over join tables?
No - the difference is physical, not syntactic. An ORM still emits joins that re-derive relationships per query; a graph store persists adjacency and walks it.
How large does a graph get before traversals slow down?
Traversal cost scales with edges visited, so the operative number is your data’s branching factor raised to the hop depth - exponential in depth, which is why bounding depth matters - not total graph size. Dense supernodes (a node with millions of edges) are the thing to model around, on any graph system.
Do I have to learn a whole new query language?
Cypher’s core - MATCH, patterns, RETURN - is learnable in an afternoon, and you keep SQL for everything set-shaped. The two coexist here deliberately.
What are the canonical graph use cases?
Fraud rings and collusion detection, recommendations, permission and dependency resolution, supply-chain tracing, knowledge graphs for agents, and agent memory - anywhere the question contains “connected to,” “through,” or “within N steps.”
What to read next
- One database, every query shape - the architectural case for one substrate.
- Per-key TTL for agent memory - agent memory patterns that pair naturally with traversal.
- How vector search actually works - the companion deep-dive on the vector query shape.