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-latest

SQL 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
SQL

3. 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:latest

5. Connect and register the backend

mgconsole --port 7688
ADD 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:

KeyEffect
NoLock=trueEvery table MemGQL reads carries WITH (NOLOCK), so federation traffic never waits on writers.
IsolationLevel=READ UNCOMMITTEDRuns 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

FeatureSQL 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 m render as ORDER BY … OFFSET m ROWS FETCH NEXT n ROWS ONLY; when the query has no ORDER BY, a neutral ORDER BY (SELECT NULL) is synthesized because T-SQL requires one for OFFSET/FETCH.
  • Booleans are emitted as BIT (1/0).
  • char_length() / length() on a column translate to T-SQL LEN().
  • INSERT … RETURN projects the inserted row through SQL Server’s OUTPUT 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.