SharePoint to ClickHouse

SharePoint list data in ClickHouse Cloud or a self-hosted server, for Grafana panels and fast aggregation over years of list history.

Book a call

Talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.

Last updated

One way: SharePoint stays the system of record, and SPSync keeps a read-only copy of your lists current in ClickHouse.

Status: Shipped · product: SPSync for Warehouses & Lakes

ClickHouse scans and aggregates large tables quickly, which makes it a good home for SharePoint lists that have grown into years of operational history. A Grafana panel that counts incidents by month, or a Metabase question over every approval since the list was created, returns in a moment, and the list data can sit beside the event and log data your ClickHouse already holds.

Connecting, and the two ports

The connection string looks like clickhouse://user:password@host:9440/database?secure=1 and lives in an environment variable. ClickHouse uses two ports:

ClickHouse has databases but no schemas, so the destination’s dataset name becomes a table-name prefix. With a dataset name of sync, a list table named ops_incidents is created as sync___ops_incidents, and several replications can share one database.

How rows are written

Each list becomes a plain MergeTree table. A run upserts changed rows by deleting the old version and inserting the new one, so the table always holds one current row per item and a query needs no FINAL modifier. Multi-value columns get child tables, one row per item and value, and every person column points at the shared sp_users table.

Soft deletes use ClickHouse’s ALTER TABLE ... UPDATE mutation with mutations_sync = 1, so the deletion is visible when the run ends. ClickHouse queues mutations per table and starts refusing writes when too many are pending; SPSync issues at most one per table per run, which matters only for a table with constant deletion churn.

Text is ClickHouse’s unbounded String, timestamps are DateTime64(6, 'UTC'), and JSON-shaped values such as a location column’s raw form are stored as String. A column added in SharePoint appears on the next run, a removed one stays, and a retyped column gets a _v2 column beside it.

Where it helps

Dashboards over long histories. Grafana or Metabase panels over years of list data; see Tableau, Looker, Metabase and Grafana on SharePoint data.

Operational screens. A wall dashboard of open incidents updates within a few minutes on the default five-minute schedule; see operational dashboards.

A warehouse source. SharePoint lists sit beside other ingested data under the same access model; see SharePoint as a governed warehouse source.

dbt. The key columns give dbt a stable source to model from; see dbt models over replicated SharePoint tables.

Setting it up

  1. Authorise SharePoint access with delegated sign-in, or per-site Sites.Selected app permissions for a sync that runs without a user.
  2. Create a ClickHouse user that can create tables, and views if you want them.
  3. Store the connection string in an environment variable.
  4. Choose the lists or a whole site and run. Turn on reporting views if your BI tool prefers a pre-joined shape with deleted rows already filtered out.
  5. Set a schedule, every few minutes up to hourly.

ClickHouse does not enforce keys, so SPSync’s upserts are what keep one row per item. SPSync copies list and library metadata, never file contents, and never writes to SharePoint.

Watch it run

Watch the full video

Chapters are in the video's description.

Watch the walkthrough: the Northwind Analytics demo lists in SharePoint, connecting to Microsoft 365 in the desktop app, picking lists, the run into an empty ClickHouse server, and the replicated tables and analytics queries in ClickHouse Play.

Desktop, Azure or command line

All three editions run the same engine, write the same tables and use the same licence key, so the choice comes down to who looks after the sync.

Desktop app

For macOS and Windows. Sign in with Microsoft, tick the lists, choose a database and sync from your own machine on the schedule you set. Delegated sign-in needs no admin unless your tenant requires admin consent for apps.

In your Azure

SPSync in your Azure, a managed application for your own subscription, is deployed with you on a call while the Marketplace listing is prepared. Once deployed it is always on, so the sync keeps running with every laptop closed.

Command line

Every replication is a YAML file you can review and commit. Run it from a server, a container or a CI pipeline, and re-run it safely any time.

Frequently asked questions

Which table engine does SPSync use in ClickHouse?

A plain MergeTree table per list. Each run upserts changed rows with a delete followed by an insert, so a query on the table already sees one current row per item, with no FINAL modifier and no wait for background merges.

How are multi-choice columns stored?

As child tables, one row per item and value, the same shape SPSync writes to every other destination, never as ClickHouse Array columns. Multi-lookup and multi-person columns use the same shape and are still being checked on a live tenant.

How are deleted SharePoint items handled?

The row stays and its sp_deleted_at column is set, using a ClickHouse mutation that completes before the run finishes. A query, or the optional reporting view, filters those rows out.

Which ports does the connection need?

Two. The native protocol port, 9440 with TLS on ClickHouse Cloud, and the HTTP port used for loading, 8443 on Cloud. A self-hosted server usually listens for HTTP on 8123, which you set as http_port in the connection string.

Is there a free trial for the ClickHouse destination?

ClickHouse is sold after a call. Get in touch and we will show it against a sample of your own lists.

Talk to us about ClickHouse

SPSync is licensed per installation and per target family, with Standard Support included. Tell us which targets you need and how many machines, and we will size it with you. See how licensing works.

Set it up

Sign in to SharePoint with the SharePoint connection guide, then follow the ClickHouse setup guide to create the database user and open the connection for ClickHouse. If the connection test fails, the troubleshooting guide lists the errors people hit and what fixes them.

Script it

Everything you can click, you can script. The CLI, YAML config and automation below reproduce this scenario exactly. See the CLI reference.

spsync auth login --client-id 00000000-0000-0000-0000-000000000000
spsync export-config --output replication.yaml --pipeline-name clickhouse --prefix clickhouse --destination-kind clickhouse --connection-string-env SPSYNC_DB_CONNECTION --dataset-name clickhouse --client-id 00000000-0000-0000-0000-000000000000 --site-url https://contoso.sharepoint.com/sites/example --all-lists
spsync doctor replication.yaml
spsync run replication.yaml
pipeline_name: clickhouse
prefix: clickhouse
auth:
  mode: delegated
  client_id: 00000000-0000-0000-0000-000000000000
  tenant_id: common
destination:
  kind: clickhouse
  connection_string_env: SPSYNC_DB_CONNECTION
  dataset_name: clickhouse
scopes:
  - site_url: https://contoso.sharepoint.com/sites/example
    mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false

Cron (ClickHouse nightly):

# every night at 02:00
0 2 * * * cd /opt/spsync && SPSYNC_DB_CONNECTION="$(cat /etc/spsync/db.env)" uv run spsync run replication.yaml >> /var/log/spsync/run.log 2>&1

GitHub Actions:

# .github/workflows/spsync-run.yml
name: SPSync
on:
  schedule:
    - cron: "0 2 * * *"
  workflow_dispatch: {}
jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v3
      - run: uv sync
      - run: uv run spsync run replication.yaml
        env:
          SPSYNC_DB_CONNECTION: ${{ secrets.SPSYNC_DB_CONNECTION }}
          SPSYNC_LICENCE_KEY: ${{ secrets.SPSYNC_LICENCE_KEY }}

Azure DevOps:

# azure-pipelines.yml
schedules:
  - cron: "0 2 * * *"
    branches: { include: [main] }
    always: true
steps:
  - script: uv sync
  - script: uv run spsync run replication.yaml
    env:
      SPSYNC_DB_CONNECTION: $(SPSYNC_DB_CONNECTION)
      SPSYNC_LICENCE_KEY: $(SPSYNC_LICENCE_KEY)

Windows (PowerShell + Task Scheduler):

# Windows Task Scheduler action (see /docs/cli/automation/)
$env:SPSYNC_DB_CONNECTION = (Get-Content C:\spsync\db.env)
uv run spsync run C:\spsync\replication.yaml
schtasks /Create /SC DAILY /ST 02:00 /TN "SPSync" /TR "powershell -File C:\spsync\run.ps1"

macOS (launchd):

# ~/Library/LaunchAgents/io.spsync.run.plist, then: launchctl load ~/Library/LaunchAgents/io.spsync.run.plist

Secrets (licence key, connection strings) come from the environment, so never write them into the YAML. See scheduling and secrets.

Book a call

Tell us about your lists and where they need to go: we will talk through your use case, show further walkthroughs or demos, and set up a trial in your own environment.