On this page

Use ClickHouse MergeTree tables

Use the clickhouse dialect to keep MergeTree declarations and typed named parameters in SQL. The example below reads the latest berth assigned to each boat through the database/sql emitter.

Declare the versioned table and queries

In sgl.yaml, select dialect: clickhouse and go.driver: database/sql for this caller. Add a versioned table to your migration:

CREATE TABLE berths (
    boat    String,
    berth   String,
    version UInt32
) ENGINE = ReplacingMergeTree(version) ORDER BY boat;

Write the insert and the current-berth query in db/queries/harbour.sql:

-- sgl.query(MoveBoat, exec)
INSERT INTO berths (boat, berth, version) VALUES ({boat:String}, {berth:String}, {version:UInt32});
-- sgl.query(CurrentBerths, many)
SELECT boat, berth, version FROM berths FINAL ORDER BY boat;

Write versions and read the current berth

Generate the package with sgl generate and create queries := store.New(database). Give later assignments a larger version. The caller writes several assignments and reads the current ones:

	moves := []store.MoveBoatParams{
		{Boat: "Kittiwake", Berth: "north quay", Version: 1},
		{Boat: "Salty Chip", Berth: "slipway", Version: 1},
		{Boat: "Kittiwake", Berth: "fish market", Version: 2},
		{Boat: "Gull Wing", Berth: "north quay", Version: 1},
		{Boat: "Kittiwake", Berth: "outer harbour", Version: 3},
	}
	for _, move := range moves {
		if err := queries.MoveBoat(ctx, move); err != nil {
			return fmt.Errorf("moving %s: %w", move.Boat, err)
		}
	}
	berths, err := queries.CurrentBerths(ctx)
	if err != nil {
		return fmt.Errorf("reading berths: %w", err)
	}
	fmt.Fprintln(output, "berths, latest version of each boat:")

Use the native clickhouse emitter when your application calls clickhouse-go’s native connection API; select the emitter before generating and adapt composite fields to its types.

Check the MergeTree queries

From the Seagull repository root, run the MergeTree example:

cd examples/clickhouse/034_clickhouse_mergetree
go test .

Set SGL_EXAMPLE_DSN to a disposable database before running this server-backed test; without it, the test skips. See running examples for connection setup.

The complete example produces:

berths, latest version of each boat:
  Gull Wing  north quay    v1
  Kittiwake  outer harbour v3
  Salty Chip slipway       v1
daily catch, summed by day and species:
  2026-08-01 crab       30 kg
  2026-08-01 mackerel  200 kg
  2026-08-02 crab       25 kg
  2026-08-02 mackerel   95 kg
  2026-08-02 pollock    45 kg
species stats, merged from both days' states:
  crab       55 kg in all, heaviest landing 30 kg
  mackerel  295 kg in all, heaviest landing 120 kg
  pollock    45 kg in all, heaviest landing 45 kg
mackerel landings of 90 kg or more:
  #1 Kittiwake  120 kg
  #4 Kittiwake  95 kg

Read the latest version explicitly

ReplacingMergeTree merges versions in the background. FINAL makes this query resolve them when it reads; inserting a newer version alone does not make every ordinary read return only that version.

Seagull reads engine clauses to model the table; it does not change ClickHouse’s merge or transaction semantics. Migration locking does not wrap schema changes in a transaction.

See the dialects/clickhouse reference for the complete contract. The example browser exposes other engine variants; use their actual SQL rather than changing only the dialect name.

Examples for this page