The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
This cookbook provides quick recipes for common ducklake operations. Each recipe is a self-contained example you can adapt for your workflow.
For a comprehensive real-world example, see the clinical trial data lake vignette.
# PostgreSQL catalog for multi-client access
attach_ducklake(
"shared_lake",
backend = "postgres",
catalog_connection_string = "dbname=ducklake_catalog host=localhost",
lake_path = "/shared/lake/data/"
)
# SQLite catalog for lightweight local multi-client setups
attach_ducklake(
"team_lake",
backend = "sqlite",
catalog_connection_string = "metadata.sqlite",
lake_path = "data_files/"
)# First write a sample CSV (in practice, you'd have an existing file)
csv_path <- file.path(vignette_temp_dir, "sample_data.csv")
write.csv(head(iris, 20), csv_path, row.names = FALSE)
# Load the CSV into the data lake
with_transaction(
create_table(csv_path, "iris_sample"),
author = "Data Engineer",
commit_message = "Load iris sample from CSV"
)
#> Transaction started.
#> Transaction committed.If your data is already in Parquet, add_data_files()
records the files in the lake in place – no copy, no rewrite, and no
collection into R. A vector of files is registered atomically in one
snapshot. This is the fast migration path from a folder of Parquet
extracts. The target table can already exist with a compatible schema,
or create = TRUE can create it directly from the Parquet
schema. Note that the lake takes ownership of the files: later
compaction may rewrite or delete them.
# Returns a lazy dplyr tbl
cars_data <- get_ducklake_table("cars")
# Use dplyr verbs
cars_data |>
filter(cyl == 6) |>
select(mpg, cyl, hp) |>
head(3)
#> # A query: ?? x 3
#> # Database: DuckDB 1.5.1 [tgerke@Darwin 25.5.0:R 4.5.2//private/var/folders/b7/664jmq55319dcb7y4jdb39zr0000gq/T/RtmpBfCpem/ducklake/ducklake8fa6461fba10.duckdb]
#> mpg cyl hp
#> <dbl> <dbl> <dbl>
#> 1 21 6 110
#> 2 21 6 110
#> 3 21.4 6 110# Fetch all data into a data.frame
cars_df <- get_ducklake_table("cars") |> collect()
head(cars_df, 3)
#> # A tibble: 3 × 12
#> mpg cyl disp hp drat wt qsec vs am gear carb kpl
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 21 6 160 110 3.9 2.62 16.5 0 1 4 4 8.93
#> 2 21 6 160 110 3.9 2.88 17.0 0 1 4 4 8.93
#> 3 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1 9.69# See all snapshots for the cars table
list_table_snapshots("cars")
#> snapshot_id snapshot_time schema_version
#> 1 1 2026-08-27 14:25:48 1
#> 2 2 2026-08-27 14:25:48 2
#> 3 7 2026-08-27 14:25:49 7
#> 4 8 2026-08-27 14:25:49 8
#> changes
#> 1 tables_created, tables_inserted_into, main.cars, 1
#> 2 tables_created, tables_dropped, tables_inserted_into, main.cars, 1, 2
#> 3 tables_altered, 2
#> 4 tables_altered, 2
#> author commit_message commit_extra_info
#> 1 Data Engineer Initial car data load <NA>
#> 2 Data Engineer Add km/L metric to cars table <NA>
#> 3 <NA> <NA> <NA>
#> 4 <NA> <NA> <NA># Query data as it existed at snapshot 1 -- before the kpl column was added
get_ducklake_table_version("cars", version = 1) |>
select(mpg, cyl, hp) |>
head(3)
#> # A query: ?? x 3
#> # Database: DuckDB 1.5.1 [tgerke@Darwin 25.5.0:R 4.5.2//private/var/folders/b7/664jmq55319dcb7y4jdb39zr0000gq/T/RtmpBfCpem/ducklake/ducklake8fa6461fba10.duckdb]
#> mpg cyl hp
#> <dbl> <dbl> <dbl>
#> 1 21 6 110
#> 2 21 6 110
#> 3 22.8 4 93with_transaction(
get_ducklake_table("cars") |>
mutate(hp_per_cyl = hp / as.numeric(cyl)) |> # Add derived metric
replace_table("cars"),
author = "Data Engineer",
commit_message = "Add horsepower per cylinder metric"
)
#> Transaction started.
#> Stored 2 column labels as column comments.
#> Transaction committed.Note: Use replace_table() for structural changes (adding
or removing columns) and the row-level operations
(rows_update(), rows_insert(),
rows_delete()) for targeted, incremental changes. Both are
fully versioned – every committed change creates a snapshot you can
time-travel back to. See vignette("modifying-tables") for
guidance on choosing between them.
list_table_snapshots()
#> snapshot_id snapshot_time schema_version
#> 1 0 2026-08-27 14:25:47 0
#> 2 1 2026-08-27 14:25:48 1
#> 3 2 2026-08-27 14:25:48 2
#> 4 3 2026-08-27 14:25:48 3
#> 5 4 2026-08-27 14:25:48 4
#> 6 5 2026-08-27 14:25:48 5
#> 7 6 2026-08-27 14:25:49 6
#> 8 7 2026-08-27 14:25:49 7
#> 9 8 2026-08-27 14:25:49 8
#> 10 9 2026-08-27 14:25:49 9
#> 11 10 2026-08-27 14:25:50 10
#> changes
#> 1 schemas_created, main
#> 2 tables_created, tables_inserted_into, main.cars, 1
#> 3 tables_created, tables_dropped, tables_inserted_into, main.cars, 1, 2
#> 4 tables_created, tables_inserted_into, main.iris_sample, 3
#> 5 tables_created, tables_inserted_into, main.efficient_cars, 4
#> 6 views_created, main.v_efficient_cars
#> 7 views_dropped, 5
#> 8 tables_altered, 2
#> 9 tables_altered, 2
#> 10 tables_created, tables_altered, inlined_insert, main.visits, 6, 6
#> 11 tables_created, tables_dropped, tables_altered, tables_inserted_into, main.cars, 2, 7, 7
#> author commit_message commit_extra_info
#> 1 <NA> <NA> <NA>
#> 2 Data Engineer Initial car data load <NA>
#> 3 Data Engineer Add km/L metric to cars table <NA>
#> 4 Data Engineer Load iris sample from CSV <NA>
#> 5 Data Analyst Load filtered car data <NA>
#> 6 <NA> <NA> <NA>
#> 7 <NA> <NA> <NA>
#> 8 <NA> <NA> <NA>
#> 9 <NA> <NA> <NA>
#> 10 <NA> <NA> <NA>
#> 11 Data Engineer Add horsepower per cylinder metric <NA># Roll cars back to snapshot 1. The restore is recorded as a new snapshot,
# so nothing is lost -- you can still time-travel to any version.
restore_table_version(
"cars",
version = 1,
author = "Data Engineer"
)
#> Transaction started.
#> Transaction committed.
#> Table "cars" restored to snapshot 1 (recorded as a new snapshot).
list_table_snapshots("cars")
#> snapshot_id snapshot_time schema_version
#> 1 1 2026-08-27 14:25:48 1
#> 2 2 2026-08-27 14:25:48 2
#> 3 7 2026-08-27 14:25:49 7
#> 4 8 2026-08-27 14:25:49 8
#> 5 10 2026-08-27 14:25:50 10
#> 6 11 2026-08-27 14:25:50 11
#> changes
#> 1 tables_created, tables_inserted_into, main.cars, 1
#> 2 tables_created, tables_dropped, tables_inserted_into, main.cars, 1, 2
#> 3 tables_altered, 2
#> 4 tables_altered, 2
#> 5 tables_created, tables_dropped, tables_altered, tables_inserted_into, main.cars, 2, 7, 7
#> 6 tables_created, tables_dropped, tables_inserted_into, main.cars, 7, 8
#> author commit_message commit_extra_info
#> 1 Data Engineer Initial car data load <NA>
#> 2 Data Engineer Add km/L metric to cars table <NA>
#> 3 <NA> <NA> <NA>
#> 4 <NA> <NA> <NA>
#> 5 Data Engineer Add horsepower per cylinder metric <NA>
#> 6 Data Engineer Restored cars to snapshot 1 <NA>with_transaction({
# All these operations happen atomically
create_table(raw_data, "raw_table")
cleaned <- get_ducklake_table("raw_table") |>
filter(!is.na(key_field)) |>
create_table("clean_table")
get_ducklake_table("clean_table") |>
mutate(derived_field = calculate_something(x)) |>
create_table("analysis_table")
},
author = "Data Engineer",
commit_message = "Full ETL pipeline run"
)To see the SQL a read pipeline will run, use dplyr’s
show_query():
get_ducklake_table("cars") |>
filter(mpg > 25) |>
select(mpg, cyl, hp) |>
show_query()
#> <SQL>
#> SELECT mpg, cyl, hp
#> FROM cars
#> WHERE (mpg > 25.0)To preview the SQL an in-place modification would run
(before committing to it with ducklake_exec()), use
show_ducklake_query():
# Good: Filter before other operations
get_ducklake_table("cars") |>
filter(cyl == 6) |>
mutate(kpl = mpg * 0.425144) |>
head(3)
#> # A query: ?? x 12
#> # Database: DuckDB 1.5.1 [tgerke@Darwin 25.5.0:R 4.5.2//private/var/folders/b7/664jmq55319dcb7y4jdb39zr0000gq/T/RtmpBfCpem/ducklake/ducklake8fa6461fba10.duckdb]
#> mpg cyl disp hp drat wt qsec vs am gear carb kpl
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 21 6 160 110 3.9 2.62 16.5 0 1 4 4 8.93
#> 2 21 6 160 110 3.9 2.88 17.0 0 1 4 4 8.93
#> 3 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1 9.10# Good: Select only needed columns
get_ducklake_table("cars") |>
select(mpg, cyl, hp) |>
filter(mpg > 25)
#> # A query: ?? x 3
#> # Database: DuckDB 1.5.1 [tgerke@Darwin 25.5.0:R 4.5.2//private/var/folders/b7/664jmq55319dcb7y4jdb39zr0000gq/T/RtmpBfCpem/ducklake/ducklake8fa6461fba10.duckdb]
#> mpg cyl hp
#> <dbl> <dbl> <dbl>
#> 1 32.4 4 66
#> 2 30.4 4 52
#> 3 33.9 4 65
#> 4 27.3 4 66
#> 5 26 4 91
#> 6 30.4 4 113For big tables, declaring a sort order or partition keys lets DuckLake skip whole Parquet files when a query filters on those columns:
set_ducklake_option() adjusts DuckLake’s persisted
settings at lake, schema, or table scope, and
get_ducklake_options() shows what’s set:
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.