SQL Server
The SQL Server connector (type sqlserver; mssql, sql_server, and
sql-server are accepted aliases) translates GQL queries into T-SQL. It
requires a mapping file that
maps graph patterns to relational tables, the same format as every other SQL
backend.
The driver is tiberius, a pure-Rust implementation of the TDS wire protocol. No ODBC driver or system packages are required at build or runtime.
SQL Server is available in
multi mode only; there is no
standalone CONNECTOR_TYPE=sqlserver environment mode yet. You register it at
runtime with ADD CONNECTOR … TYPE sqlserver, as shown below.
1. Start SQL Server
docker network create memgql-net
docker run -d --rm \
--name sqlserver-dev \
--network memgql-net \
-p 1433:1433 \
--env ACCEPT_EULA=Y \
--env MSSQL_SA_PASSWORD='Memgraph!2024' \
--env MSSQL_PID=Developer \
mcr.microsoft.com/mssql/server:2022-latestSQL Server takes ~20 seconds to accept connections. Then create a working database:
docker exec sqlserver-dev /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'Memgraph!2024' -C \
-Q "IF DB_ID('test') IS NULL CREATE DATABASE [test]"2. Seed data
docker exec -i sqlserver-dev /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'Memgraph!2024' -C -b -d test << 'SQL'
CREATE TABLE persons (
id INT IDENTITY(1,1) PRIMARY KEY,
name NVARCHAR(255) NOT NULL,
age INT
);
CREATE TABLE companies (
id INT IDENTITY(1,1) PRIMARY KEY,
name NVARCHAR(255) NOT NULL
);
CREATE TABLE knows (
id INT IDENTITY(1,1) PRIMARY KEY,
from_id INT NOT NULL REFERENCES persons(id),
to_id INT NOT NULL REFERENCES persons(id)
);
CREATE TABLE works_at (
id INT IDENTITY(1,1) PRIMARY KEY,
person_id INT NOT NULL REFERENCES persons(id),
company_id INT NOT NULL REFERENCES companies(id)
);
INSERT INTO persons (name, age) VALUES ('Alice', 30), ('Bob', 25);
INSERT INTO companies (name) VALUES ('Acme Corp');
INSERT INTO knows (from_id, to_id) VALUES (1, 2);
INSERT INTO works_at (person_id, company_id) VALUES (1, 1);
GO
SQL3. Write the mapping
Save as mapping.json, the standard
mapping format. Declare every
property you plan to query: in multi mode the mapping is also the routing
schema, and a property that isn’t declared is a routing error, not a
passthrough.
{
"vertices": [
{
"label": "Person",
"mappedTableSource": {
"connector": "ss",
"table": "persons",
"metaFields": {
"id": "id"
}
},
"attributes": [
{
"name": "name"
},
{
"name": "age",
"type": "Int"
}
]
},
{
"label": "Company",
"mappedTableSource": {
"connector": "ss",
"table": "companies",
"metaFields": {
"id": "id"
}
},
"attributes": [
{
"name": "name"
}
]
}
],
"edges": [
{
"label": "KNOWS",
"from": "Person",
"to": "Person",
"mappedTableSource": {
"connector": "ss",
"table": "knows",
"metaFields": {
"id": "id",
"from": "from_id",
"to": "to_id"
}
}
},
{
"label": "WORKS_AT",
"from": "Person",
"to": "Company",
"mappedTableSource": {
"connector": "ss",
"table": "works_at",
"metaFields": {
"id": "id",
"from": "person_id",
"to": "company_id"
}
}
}
]
}4. Start MemGQL in multi mode
docker run --rm \
--name memgql \
--network memgql-net \
--stop-timeout 2 \
-p 7688:7688 \
--env CONNECTOR_TYPE=multi \
-v ./mapping.json:/data/mapping.json \
memgraph/memgql:latest5. Connect and register the backend
mgconsole --port 7688ADD CONNECTOR ss TYPE sqlserver
URI 'Server=sqlserver-dev,1433;Database=test;User Id=sa;Password=Memgraph!2024;TrustServerCertificate=true';
CREATE GRAPH social FROM FILE '/data/mapping.json';
MATCH (n:Person) RETURN n.name LIMIT 5;The URI is an ADO-style connection string
(Server=<host>,<port>;Database=<db>;User Id=<user>;Password=<pass>;…).
Encryption is on by default and the server certificate is validated against
the system trust store; TrustServerCertificateCA=<pem> adds a private CA and
TrustServerCertificate=true accepts a self-signed certificate, as above. Two
MemGQL-specific keys may be added for an instance that must never wait on
MemGQL:
| Key | Effect |
|---|---|
NoLock=true | Every table MemGQL reads carries WITH (NOLOCK), so federation traffic never waits on writers. |
IsolationLevel=READ UNCOMMITTED | Runs SET TRANSACTION ISOLATION LEVEL once per session; any T-SQL level is accepted. |
Mappings may name a view as readily as a table. Add "schema": "reporting"
for a non-default schema and "database": "warehouse" to reach another
database through the same connection (rendered warehouse.reporting.orders_v),
and give metaFields.id / from / to a list of columns for a composite key —
see the schema file reference.
6. Query
MATCH (p:Person) RETURN p.name, p.age;MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p.name, c.name;MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name;Supported GQL features
| Feature | SQL Server |
|---|---|
MATCH / WHERE / RETURN | ✓ |
Pattern-level WHERE (MATCH (n WHERE …)) | ✓ |
Multiple MATCH clauses in one query | ✓ |
OPTIONAL MATCH | ✓ |
WITH clause (chain query steps) | ✓ |
WITH DISTINCT / WITH … ORDER BY … LIMIT N | ✓ |
Multiple chained WITH steps | ✓ |
Pass a whole node through WITH n | ✓ |
Typed edge (a)-[r:R]->(b) | ✓ |
Untyped edge ()-[]->(b) | ✗ |
UNION / UNION ALL / UNION DISTINCT | ✓ |
INTERSECT / EXCEPT | ✓ |
EXISTS { … } / pattern predicates | ✓ |
CALL { … } subquery | ✓ |
Quantified path (){m,n} / *m..n (bounded) | ✗ |
Whole-node RETURN n / whole-rel RETURN r | ✓ |
Map projections RETURN n {.a, .b} | ✓ |
IN list membership WHERE x IN [...] | ✓ |
STARTS WITH / ENDS WITH / CONTAINS | ✓ |
collect() aggregate | ✗ |
count, sum, avg, min, max | ✓ |
COUNT(DISTINCT …) | ✓ |
Arithmetic + - * / % | ✓ |
CASE WHEN … THEN … ELSE … END | ✓ |
COALESCE, NULLIF | ✓ |
CAST(x AS STRING | INT | FLOAT | BOOLEAN) | ✓ |
Scalar functions (upper, abs, char_length, …) | ✓ |
Cypher functions (toUpper, substring, toInteger, …) | ✓ |
Query parameters ($x) | ✓ |
date('YYYY-MM-DD') returns a Date | ✗ |
FOR x IN [...] / UNWIND | ✗ |
INSERT (a {…}), incl. an edge between matched nodes | ✓ |
INSERT (a {…}) RETURN a.x | ✓ |
DELETE | ✓ |
SET / REMOVE (property update / delete) | ✗ |
DETACH DELETE | ✗ |
Quantified path (){m,} (unbounded) | ✗ |
Shortest-path (ALL SHORTEST / SHORTEST k) | ✗ |
Dialect notes
- Positional bind parameters use SQL Server’s native
@P1,@P2, … form. LIMIT n/SKIP mrender asORDER BY … OFFSET m ROWS FETCH NEXT n ROWS ONLY; when the query has noORDER BY, a neutralORDER BY (SELECT NULL)is synthesized because T-SQL requires one forOFFSET/FETCH.- Booleans are emitted as
BIT(1/0). char_length()/length()on a column translate to T-SQLLEN().INSERT … RETURNprojects the inserted row through SQL Server’sOUTPUT INSERTED.<col>clause, so it returns the values, not a row count.
Known limitations
- Variable-length paths: quantified path patterns (
(){1,3}) and trail semantics are not available on SQL Server yet and return an actionable error. Use a bounded fixed-hop chain or a Cypher backend. collect()is not available; there is no portable array aggregate in the T-SQL surface MemGQL emits yet; the query fails with an actionable error.- Temporal functions (
date(),datetime(), …) return an actionable error.
For connector configuration, see Reference.