SQL/PGQ — Graph Pattern Matching
Create property graphs over existing tables and query them with Cypher-like MATCH patterns, composable with full SQL analytics.
CREATE PROPERTY GRAPH
Define a property graph by declaring vertex tables and edge tables over existing relational tables. Teide automatically builds compressed sparse row (CSR) indexes on creation for efficient traversal.
Syntax:
CREATE PROPERTY GRAPH graph_name
VERTEX TABLES (
table_name
KEY (column_name)
LABEL label_name
PROPERTIES (col1, col2, ...)
)
EDGE TABLES (
table_name
KEY (column_name)
SOURCE KEY (src_col) REFERENCES vertex_table (key_col)
DESTINATION KEY (dst_col) REFERENCES vertex_table (key_col)
LABEL label_name
PROPERTIES (col1, col2, ...)
);
Example — a social network with persons and friendships:
-- Relational tables
CREATE TABLE persons (id INTEGER, name VARCHAR, age INTEGER);
INSERT INTO persons VALUES (1, 'Alice', 30), (2, 'Bob', 25),
(3, 'Carol', 35), (4, 'Dave', 28);
CREATE TABLE friendships (id INTEGER, src INTEGER, dst INTEGER, since DATE);
INSERT INTO friendships VALUES (1, 1, 2, '2020-01-15'),
(2, 1, 3, '2019-06-01'),
(3, 2, 4, '2021-03-22'),
(4, 3, 4, '2022-11-10');
-- Property graph (builds CSR indexes automatically)
CREATE PROPERTY GRAPH social
VERTEX TABLES (
persons KEY (id) LABEL Person PROPERTIES (name, age)
)
EDGE TABLES (
friendships KEY (id)
SOURCE KEY (src) REFERENCES persons (id)
DESTINATION KEY (dst) REFERENCES persons (id)
LABEL Knows
PROPERTIES (since)
);
DROP PROPERTY GRAPH
DROP PROPERTY GRAPH social;
DROP PROPERTY GRAPH IF EXISTS social;
GRAPH_TABLE with MATCH
Use GRAPH_TABLE in a FROM clause to run pattern matching queries against a property graph. The MATCH clause specifies the graph pattern, and the COLUMNS clause projects properties out as relational columns.
Basic 1-Hop Pattern
SELECT *
FROM GRAPH_TABLE (social
MATCH (a:Person)-[:Knows]->(b:Person)
COLUMNS (a.name AS person, b.name AS friend)
);
-- Alice | Bob
-- Alice | Carol
-- Bob | Dave
-- Carol | Dave
With WHERE Filter
Predicates can be placed directly inside vertex or edge patterns:
SELECT *
FROM GRAPH_TABLE (social
MATCH (a:Person WHERE a.name = 'Alice')-[:Knows]->(b:Person)
COLUMNS (a.name AS person, b.name AS friend, b.age AS friend_age)
);
-- Alice | Bob | 25
-- Alice | Carol | 35
COLUMNS Clause
The COLUMNS clause maps graph element properties to output columns. Each entry follows the form element.property AS alias:
SELECT *
FROM GRAPH_TABLE (social
MATCH (a:Person)-[e:Knows]->(b:Person)
COLUMNS (a.name AS src, b.name AS dst, e.since AS friends_since)
);
Variable-Length Paths
Quantifiers on edge patterns enable multi-hop traversal.
Bounded Range {min,max}
-- Friends within 1 to 3 hops
SELECT *
FROM GRAPH_TABLE (social
MATCH (a:Person WHERE a.name = 'Alice')
-[:Knows]->{1,3}
(b:Person)
COLUMNS (a.name AS src, b.name AS reachable)
);
-- Alice | Bob
-- Alice | Carol
-- Alice | Dave
One or More +
-- Transitive closure (1+ hops)
SELECT *
FROM GRAPH_TABLE (social
MATCH (a:Person)-[:Knows]->+(b:Person)
COLUMNS (a.name AS src, b.name AS reachable)
);
Zero or More *
-- Reflexive transitive closure (0+ hops, includes self)
SELECT *
FROM GRAPH_TABLE (social
MATCH (a:Person)-[:Knows]->*(b:Person)
COLUMNS (a.name AS src, b.name AS reachable)
);
Shortest Path
Use ANY SHORTEST to find a single shortest path between nodes. Bind the path to a variable to use path_length().
SELECT *
FROM GRAPH_TABLE (social
MATCH ANY SHORTEST
(a:Person WHERE a.name = 'Alice')
-[p:Knows]->+
(b:Person WHERE b.name = 'Dave')
COLUMNS (a.name AS src, b.name AS dst, path_length(p) AS hops)
);
-- Alice | Dave | 2
Edge Directions
Patterns support three edge directions:
-- Forward (outgoing edges)
MATCH (a:Person)-[:Knows]->(b:Person)
-- Reverse (incoming edges)
MATCH (a:Person)<-[:Knows]-(b:Person)
-- Undirected (either direction)
MATCH (a:Person)-[:Knows]-(b:Person)
Composable with SQL
GRAPH_TABLE returns a standard relation, so it composes freely with all SQL constructs: GROUP BY, ORDER BY, JOIN, window functions, CTEs, and subqueries.
Top 10 Most Connected People
SELECT person, COUNT(*) AS connections
FROM GRAPH_TABLE (social
MATCH (a:Person)-[:Knows]-(b:Person)
COLUMNS (a.name AS person)
)
GROUP BY person
ORDER BY connections DESC
LIMIT 10;
Graph Results as CTE, Joined with Other Tables
WITH reachable AS (
SELECT *
FROM GRAPH_TABLE (social
MATCH (a:Person WHERE a.name = 'Alice')
-[:Knows]->{1,3}
(b:Person)
COLUMNS (b.name AS friend_name, b.age AS friend_age)
)
)
SELECT r.friend_name, r.friend_age, o.city
FROM reachable r
INNER JOIN locations o ON r.friend_name = o.name
ORDER BY r.friend_age;