Moving Data Between Databases with DBeaver's Data Transfer Wizard
DBeaver's Data Transfer wizard moves data between any two JDBC databases without scripts. Here's a worked SQLite-to-PostgreSQL example, the mapping pitfalls to watch, and how to verify the result.
01 Jul 2026, 10:33 UTC

You've got a table living in SQLite (or MySQL, or a legacy Oracle box) and it needs to land in PostgreSQL. The classic answer is "write an export script, massage the CSV, write an import script, debug the encoding." DBeaver's built-in Data Transfer wizard collapses most of that into a guided dialog — and because it speaks JDBC on both ends, it works between almost any two databases without extra tooling.
The takeaway: for one-off or occasional cross-database copies, the wizard is genuinely good. For repeatable, production-grade migrations, it's a starting point, not the whole plan. Here's how to use it well, and where it stops helping.
What the wizard actually does
Data Transfer reads rows from a source connection, applies optional filtering and column mapping, and batches inserts into a target connection. It can create the target tables for you (inferring types from the source) or write into existing tables. Because both ends are just JDBC connections, "heterogeneous" pairs — SQLite to PostgreSQL, MySQL to SQL Server, Oracle to MariaDB — are all the same workflow.
Transfers run in the background with a progress panel, and the executed SQL and row counts land in a log you can export. That log matters more than people expect: it's your audit trail and your debugging tool when a type conversion goes sideways.
A worked example: SQLite to PostgreSQL
Assume you have two connections already configured in DBeaver (a local SQLite file and a PostgreSQL 15 server) and you want to copy an orders table, but only recent rows.
- In the Database Navigator, expand the SQLite connection, right-click the
orderstable, and choose Export Data. (You can also multi-select tables, or right-click a schema to filter tables by regex later.) - In the wizard, choose Database as the export target type — this is the step that makes it a database-to-database transfer rather than a CSV dump.
- On the source screen, add a filter. You can write a WHERE-style condition such as
created_at >= '2026-01-01', or supply a fully custom SELECT if you need joins or computed columns. - On the target screen, pick your PostgreSQL connection and schema. Choose whether DBeaver should create the table or map onto an existing one.
- Open the column mapping. Check the inferred types — a common fix is SQLite's loose typing producing
TEXTwhere you wantNUMERICorTIMESTAMPin Postgres. You can override target column names, types, and default values here. - Review the extraction and load settings: fetch size, batch size, and whether to commit in chunks. For a few hundred thousand rows, the defaults are usually fine.
- Run it and watch the Data Transfer view. When it finishes, open the log and confirm the row count matches your filter's expected count.
Verify on the target side with a quick query in a DBeaver SQL editor connected to PostgreSQL:
SELECT count(*), min(created_at), max(created_at)
FROM orders;Compare the count against a SELECT count(*) FROM orders WHERE created_at >= '2026-01-01' on the SQLite source. If they match and the min/max bounds look right, the transfer did what you asked.
Getting the column mapping right
The mapping screen is where most transfers succeed or fail. Three habits pay off:
- Check every inferred type on cross-engine transfers. SQLite's dynamic typing and MySQL's
TINYINT(1)booleans are frequent sources of surprises. - Use defaults deliberately. If the target has a NOT NULL column the source lacks, set a default in the mapping rather than letting the insert fail at row 40,000.
- Prefer a custom SELECT over post-hoc cleanup. If you need to rename, cast, or drop columns, doing it in the source query keeps the mapping trivial and the log readable.
Trade-offs and limits you should know
The wizard copies data and table structure, not the database. Triggers, stored procedures, views, and user-defined functions don't come along — you recreate those manually in the target dialect. Foreign keys are the other classic gotcha: the wizard doesn't resolve insert ordering, so a multi-table transfer can fail on constraint violations. The practical fixes are to disable or defer FK constraints in the target during the load (and re-enable after), or to transfer parent tables before children.
Performance is decent but not magical. Chunked, batched inserts help a lot, but a transfer across a slow network is still bound by round trips. For very large datasets, a native bulk loader (like PostgreSQL's COPY from a CSV export) will usually win on raw speed — at the cost of losing the wizard's type mapping and convenience. Also note the wizard is an interactive tool; if you need a scheduled, repeatable pipeline, you'll outgrow it and want a scripted or ETL-based approach.
Before you trust the result
Never treat "the wizard finished without errors" as proof. Do three checks: row counts per table on both sides, a spot-check of a few rows with tricky values (NULLs, timestamps, decimals, non-ASCII text), and a review of the exported transfer log to confirm the filters and mappings you configured are what actually ran. If you disabled foreign keys for the load, re-enable them and run an integrity check before calling it done.
Used this way, Data Transfer turns a half-day scripting chore into a twenty-minute task — just keep its boundaries in mind, and verify like you would with any migration.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.