데이터 가져오기

데이터 가져오기 (Import Data)

Rust 클라이언트에는 데이터를 DuckDB에 벌크로 넣는 두 가지 방식이 있어요. Rust에서 행을 삽입하는 고성능 Appender와, Parquet·CSV·JSON 같은 데이터 파일에서 직접 읽는 방식이에요. 둘 다 아래에서 살펴볼게요.

출처: 문서

본문

Appender

[Appender]({% link docs/current/data/appender.md %})는 Rust에서 생성한 행을 삽입하는 가장 빠른 방법이에요. Connection에서 appender()로 만들고 대상 테이블 이름을 넘긴 뒤 append_row()로 행을 밀어 넣어요. 각 행은 params! 매크로로 만든 파라미터 목록이에요.

use duckdb::{params, Connection, Result};

fn insert_rows(conn: &Connection) -> Result<()> {
    conn.execute_batch("CREATE TABLE foo (a INTEGER, b INTEGER)")?;

    let mut app = conn.appender("foo")?;
    app.append_row(params![1, 2])?;
    app.append_row(params![3, 4])?;
    app.flush()?;
    Ok(())
}

파라미터 목록의 이터레이터에서 많은 행을 한 번에 추가하려면 append_rows()를 사용해요.

app.append_rows([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])?;

추가 생성자는 기본 스키마 밖의 테이블을 대상으로 해요. appender_to_db()는 스키마 이름을, appender_to_catalog_and_db()는 카탈로그와 스키마를 받아요. 열의 일부만 추가하려면 appender_with_columns()(및 그 스키마·카탈로그 변형)을 사용해요.

Warning DuckDB는 추가된 행을 버퍼링하고, NOT NULL이나 외래 키 같은 제약 조건은 버퍼가 플러시될 때만 확인해요. 그래서 append_row()가 한 행에 대해 Ok를 반환해도 나중에 제약 조건 검사에서 실패할 수 있어요. Appender가 drop되기 전에 항상 flush()를 명시적으로 호출하고 그 결과를 확인하세요. Appender를 drop하면 남은 행이 플러시되지만, 그 암묵적 플러시 중에 발생한 오류는 버려져요. Rust의 Drop이 그것을 보고할 수 없기 때문이에요.

AppenderConnection을 빌리기 때문에 Sync지만 Send는 아니에요. 다른 스레드에서 추가하려면 Connection을 그 스레드로 옮기고 거기서 appender를 만들면 돼요.

트랜잭션 안에서 추가하기

대량 로드의 경우 추가를 트랜잭션으로 감싸면 DuckDB가 그것들을 단일 단위로 커밋하게 해요. Connection::transaction()으로 트랜잭션을 시작하고, drop 시 커밋하도록 설정한 뒤, 트랜잭션에서 appender를 만들어요. 다음은 크레이트의 appender 예시에서 가져와 1천만 행을 벌크 삽입하는 코드예요.

use duckdb::{params, Connection, DropBehavior, Result};

fn main() -> Result<()> {
    let mut db = Connection::open_in_memory()?;
    db.execute_batch(
        "CREATE TABLE test (id INTEGER NOT NULL, area CHAR(6), age TINYINT NOT NULL, active TINYINT NOT NULL)",
    )?;

    let row_count = 10_000_000;
    {
        let mut tx = db.transaction()?;
        tx.set_drop_behavior(DropBehavior::Commit);
        let mut app = tx.appender("test")?;
        for i in 0..row_count {
            app.append_row(params![i, "123456", 15, 1])?;
        }
    } // the appender flushes and the transaction commits here, on drop

    let count = db.query_row("SELECT count(*) FROM test", [], |row| row.get::<_, i64>(0))?;
    assert_eq!(count, row_count);
    Ok(())
}

Transaction은 drop될 때 기본적으로 롤백돼요. set_drop_behavior(DropBehavior::Commit)를 호출하면 drop 시 커밋하도록 하거나, tx.commit()을 명시적으로 호출하면 돼요. 다른 DropBehavior 변형은 Rollback(기본), Ignore(트랜잭션을 열어 둠), Panic이에요.

Arrow 데이터 추가

appender-arrow 기능을 켜면 Appender가 Apache Arrow RecordBatch를 직접 추가할 수 있는데, 행마다 파라미터 목록을 만들지 않아도 돼요. Arrow 통합은 [Handle Results]({% link docs/current/clients/rust/result_handling.md %}#apache-arrow)를 참고하세요.

데이터 파일 읽기

DuckDB는 많은 파일 포맷을 SQL에서 직접 읽을 수 있어서, 외부 데이터를 로드하는 가장 간단한 방법인 경우가 많아요. Rust 클라이언트는 그저 쿼리를 보내면 돼요. [Parquet]({% link docs/current/data/parquet/overview.md %})의 경우 parquet 기능을 켜고 read_parquet()를 쿼리해요. 크레이트의 parquet 예시에서 가져온 코드예요.

use duckdb::{Connection, Result};
use duckdb::arrow::record_batch::RecordBatch;

let conn = Connection::open_in_memory()?;
conn.execute_batch("INSTALL parquet; LOAD parquet;")?;

let batches: Vec<RecordBatch> = conn
    .prepare("SELECT * FROM read_parquet(?)")?
    .query_arrow(["data.parquet"])?
    .collect();

같은 패턴으로 read_csv()는 [CSV]({% link docs/current/data/csv/overview.md %}), read_json()은 [JSON]({% link docs/current/data/json/overview.md %})을 읽어요(후자는 json 기능 필요). 파일을 그 자리에서 쿼리하는 대신 테이블로 로드하려면 리더를 [CREATE TABLE ... AS SELECT]({% link docs/current/sql/statements/create_table.md %}#create-table--as-select-ctas)나 execute()로 보낸 [COPY]({% link docs/current/sql/statements/copy.md %}) 문으로 감싸면 돼요.

더 알아보기 (Learn more)

  • [Appender]({% link docs/current/data/appender.md %}) — Rust Appender가 감싸는 엔진 레벨 Appender 인터페이스.
  • [Data Import]({% link docs/current/data/overview.md %}) — DuckDB의 벌크 로딩 옵션 전체.
  • [Handle Results]({% link docs/current/clients/rust/result_handling.md %}) — record batch를 추가·읽는 데 쓰는 Apache Arrow 통합.
  • [Run Queries]({% link docs/current/clients/rust/querying.md %}) — 파일 리더가 기반으로 하는 CREATE TABLECOPY 문 보내기.