The short answer
A database cannot read an .xlsx file by itself. Something has to read the sheet, decide what type each column is, create a table and insert the rows. You can let your database's import wizard do it, load a CSV with a bulk command, or generate plain SQL that any database client can run.
Generated SQL is the most portable option and the easiest to check, because you see exactly what will be created before anything touches the database. In the converter below, open the .xlsx or .csv file (or paste cells copied from Excel), choose the Database, type the Table name, and copy or download the script. Column types are detected from the data, and barcodes or codes with leading zeros stay text.
Try it here: Excel to SQL
Open the full tool pagePrepare the sheet first
Most failed imports are caused by the spreadsheet, not the database. Spreadsheets are made for people; tables need one clean rectangle of data. Spend two minutes on this before importing anything:
- Exactly one header row with unique column names. Remove titles above the table, merged header cells and second header rows.
- One row per record. Delete total and subtotal rows, blank separator rows and notes below the data.
- One kind of value per column. A Price column should not contain the text on request in some rows; use an empty cell instead, which becomes NULL.
- No merged cells inside the data. Unmerge them and fill the value into every row it applies to.
- Formulas are fine: the last calculated value saved in the file is used. Make sure the workbook was recalculated and saved after the last change.
- Put each table on its own sheet. When a workbook has several sheets, the converter shows a Sheet selector.
Methods compared
| Method | Good for | Watch out for |
|---|---|---|
| Generated SQL (this page) | Up to tens of thousands of rows, any database, reviewable script | Very large files make very large scripts |
| SQL Server Import and Export Wizard | Regular imports into SQL Server | Reading .xlsx needs the Microsoft Access Database Engine; 32 bit and 64 bit versions must match |
| SSMS Import Flat File | CSV into SQL Server | Check the guessed types before finishing |
| BULK INSERT (SQL Server), LOAD DATA (MySQL), COPY (PostgreSQL) | Millions of rows from CSV | File access rights on the server, delimiter and encoding options |
| MySQL Workbench Table Data Import Wizard | CSV or JSON into MySQL | Slow on large files |
| pgAdmin Import/Export | CSV into an existing PostgreSQL table | The table must exist first |
| The .import command of the sqlite3 shell (csv mode) | CSV into SQLite | Columns are created as TEXT unless the table exists |
Step by step with generated SQL
- Open the file or paste the cells. The first row becomes the column names; turn off First row is header if your data has none.
- Choose the Database: SQL Server, MySQL, PostgreSQL or SQLite. Quoting, types and escaping change with it.
- Type the Table name. With Simplify names on, Product Name becomes product_name, which saves you from quoting names in every later query.
- Pick Rows per INSERT. 100 is a safe default; larger batches import faster. SQL Server accepts at most 1000 rows in one VALUES list, so the tool never goes above that for SQL Server.
- Decide on the extras: CREATE TABLE writes the table definition, Drop table first removes an existing table with the same name (only use it on tables you mean to replace), and Use a transaction makes the import all or nothing.
- Copy the SQL or download the .sql file, then run it in your database client.
Column types the converter chooses
Each column gets the narrowest type that fits every value in it. Text columns get a length rounded up from the longest value (for example VARCHAR(50) for a 37 character maximum), so you can still widen them later if needed.
| Data in the column | SQL Server | MySQL | PostgreSQL | SQLite |
|---|---|---|---|---|
| Whole numbers | INT or BIGINT | INT or BIGINT | INTEGER or BIGINT | INTEGER |
| Decimals | DECIMAL(p, s) | DECIMAL(p, s) | NUMERIC(p, s) | REAL |
| true or false | BIT | TINYINT(1) | BOOLEAN | INTEGER |
| Dates | DATE | DATE | DATE | TEXT |
| Date and time | DATETIME2 | DATETIME | TIMESTAMP | TEXT |
| Text, codes with leading zeros | NVARCHAR(n) | VARCHAR(n) | VARCHAR(n) | TEXT |
Common problems and how to avoid them
- Dates arrive as numbers like 45000. Excel stores dates as day counts (45000 is 15 March 2023). When you read an .xlsx with the converter, date cells are turned back into real dates; other tools may need the column formatted as a date first.
- Leading zeros disappear. Barcodes, postal codes and product codes like 00123 must be text. The converter keeps a column as text when its values start with zero, and numbers longer than 15 digits also stay text.
- Accented letters become question marks in SQL Server. Unicode text needs NVARCHAR columns and the N prefix on string literals; the generated SQL Server script uses both. In MySQL, use a utf8mb4 table.
- Decimal comma numbers. In semicolon separated CSV files, values like 1.499,90 are read as 1499.90. In comma separated files the comma is a column separator, so such values must be quoted.
- Empty cells. They become NULL and the column is created as nullable. If you want empty text instead, change it after the import.
- Quotes inside text. An apostrophe in O'Brien would break a hand written INSERT; generated values are escaped, and MySQL backslashes are escaped too.
When to use a bulk loader instead
INSERT scripts are perfect for a few thousand to tens of thousands of rows: you can read them, keep them in version control and run them anywhere. For hundreds of thousands or millions of rows, the script gets very large and running it is slower than a bulk load.
In that case, save the sheet as CSV UTF-8 and use the bulk command of your database: BULK INSERT in SQL Server, LOAD DATA LOCAL INFILE in MySQL, COPY or psql's \copy in PostgreSQL. A useful middle path is to let the converter write only the CREATE TABLE statement from a sample of the data, so you get sensible column types, and then load the full CSV into that table with the bulk command.