duckdb имеет функцию IMPORT DATABASE 'folder'.
Сначала выполняется schema.sql. Этот файл содержит множество утверждений CREATE TABLE.
Затем выполняется файл load.sql. Этот файл содержит множество операторов COPY, которые импортируют, например. CSV-файлы.
Интересно, смогу ли я проверить, что заголовки CSV-файла совпадают с именами полей в операторах CREATE TABLE. Я использую R, поэтому минимальный пример выглядит так:
# write example schema.sql, load.sql, and iris.csv
folder <- tempdir()
curr_wd <- getwd()
#on.exit(setwd(curr_wd))
setwd(folder)
dir.create("dbdump") |> suppressWarnings()
write.csv(iris, "dbdump/iris.csv", row.names=FALSE)
writeLines(paste(
"CREATE TABLE iris(",
" sepal_length DOUBLE,",
" sepal_width DOUBLE,",
" petal_length DOUBLE,",
" petal_width DOUBLE,",
" species VARCHAR);", sep = "\n"), "dbdump/schema.sql"
)
writeLines(
"COPY iris FROM 'iris.csv' (DELIMITER ',', HEADER);",
"dbdump/load.sql"
)
Теперь я могу импортировать данные с помощью
library(duckdb)
con <- DBI::dbConnect(drv=duckdb::duckdb(), dbdir = ":memory:")
#on.exit(DBI::dbDisconnect(con, shutdown=TRUE))
DBI::dbExecute(con, "IMPORT DATABASE 'dbdump';")
Если я проверю имена столбцов,
DBI::dbReadTable(con, "iris") |> str()
#'data.frame': 150 obs. of 5 variables:
# $ sepal_length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
# $ sepal_width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
# $ petal_length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
# $ petal_width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
# $ species : chr "setosa" "setosa" "setosa" "setosa" ...
Используются имена полей из оператора CREATE TABLE. Однако у iris.csv другой заголовок:
colnames(iris)
# [1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width"
# [5] "Species"
Я бы хотел получить предупреждение в этом случае. Особенно меня беспокоит то, что порядок столбцов в CSV отличается от оператора CREATE TABLE и остается незамеченным, если тип тот же.
Нужно ли программировать эту проверку вручную или есть более удобный способ?
Посетите github.com/duckdb/duckdb/issues/3506


Для справки: в итоге я написал свой собственный тест. В примере вопроса:
writeLines(paste(
"stopifnot(",
" all(colnames(read.csv('dbdump/iris.csv', nrows=1)) = = ",
" c('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'))",
")", sep = "\n"), "dbdump/check_headers.R"
)
и затем, прежде чем вызвать IMPORT DATABASE:
source("dbdump/check_headers.R")
Из комментариев я узнал, что SQL предлагает явное именование столбцов, поэтому мой load.sql теперь выглядит так:
writeLines(paste(
"COPY iris(sepal_length, sepal_width, petal_length, petal_width, species)",
"FROM 'iris.csv' (DELIMITER ',', HEADER);"), "dbdump/load.sql"
)
Таблица уже создана с новыми именами столбцов, «HEADER» внутри COPY просто означает «есть заголовок, игнорируя первую строку». Я думаю, нам потребуется вручную проверить правильность перекрытия имен столбцов.