PHP Import Excel File into MySQL Database with PhpSpreadsheet

Importing Excel data into MySQL is a common requirement in admin panels, internal tools, and bulk data upload screens. In PHP, the most practical way to do this is to upload the Excel file, read it with PhpSpreadsheet, validate each row, and insert the data into MySQL using prepared statements.

In this tutorial, I will show a simple working example that imports an Excel file into a MySQL table using plain PHP and MySQLi. The demo accepts .xlsx and .xls files, skips the header row, validates important fields, and shows the imported rows after processing.

Quick Answer

To import an Excel file into MySQL using PHP:

  • Upload the Excel file using a regular HTML form.
  • Read the workbook with PhpSpreadsheet.
  • Loop through the worksheet rows.
  • Validate the required columns.
  • Insert the data into MySQL with MySQLi prepared statements.

That is the full flow we will build in this example.

What this example covers

  • Excel file upload with PHP
  • Reading .xlsx and .xls files
  • Skipping the heading row
  • Basic row validation
  • MySQL insert using prepared statements
  • Showing import success, skipped rows, and latest imported records

Before you start

This example uses:

  • PHP 8+
  • MySQL
  • MySQLi
  • PhpSpreadsheet

If you are new to handling uploads in PHP, you may also like this related guide on uploading files using PHP. Since this tutorial inserts user-provided data into a database, using prepared statements is important.

In the next section, we will create the database table and look at the project structure.

Database table

For this tutorial, we will import Excel rows into a simple tbl_users table. It stores the user’s name, email, phone, and created date.

CREATE TABLE `tbl_users` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(100) NOT NULL,
  `email` VARCHAR(150) NOT NULL,
  `phone` VARCHAR(30) DEFAULT NULL,
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

The unique email key is useful here. It prevents duplicate email entries during repeated imports. In a real project, you can adjust the columns based on your Excel format.

Project structure

The downloadable project keeps the file structure small and easy to follow.

excel-import-project/
│── config.php
│── index.php
│── import.php
│── list.php
│── schema.sql
│── composer.json
│── README.md
│── uploads/
└── sample/
    └── sample-users.xlsx
  • config.php contains the database connection.
  • index.php shows the upload form and result messages.
  • import.php validates the uploaded file, reads the Excel sheet, and inserts rows into MySQL.
  • list.php displays the latest imported records.
  • schema.sql creates the database table.
  • sample-users.xlsx gives you a ready test file.

Install PhpSpreadsheet

This tutorial uses PhpSpreadsheet to read the Excel file. Install it with Composer inside your project folder.

composer require phpoffice/phpspreadsheet

After installation, your project will include Composer’s vendor/autoload.php file. We will use that to load the library in the import script.

Sample Excel format

The Excel file used in this tutorial contains one header row and then the actual data rows.

Name | Email | Phone
John Doe | john@example.com | 9876543210
Jane Smith | jane@example.com | 9123456780

We will treat the first row as the heading row and skip it during import.

Sample Excel file with name email and phone columns for PHP import

Sample Excel sheet used for PHP MySQL import

In the next section, we will create the database connection file and then build the upload form.

Create the database connection

Start with a small configuration file for the MySQL connection. This keeps the database code separate and easier to reuse.

<?php
declare(strict_types=1);

$host = 'localhost';
$dbname = 'test';
$username = 'root';
$password = '';

$mysqli = new mysqli($host, $username, $password, $dbname);

if ($mysqli->connect_error) {
    die('Database connection failed: ' . $mysqli->connect_error);
}

$mysqli->set_charset('utf8mb4');

Save this as config.php.

Create the upload form

Next, create the main page with a simple file upload form. This page also shows success and error messages after the import is complete.

<?php
declare(strict_types=1);

$successMessage = $_GET['success'] ?? '';
$errorMessage = $_GET['error'] ?? '';
?>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Import Excel File into MySQL</title>
    <link rel="stylesheet" href="assets/style.css">
</head>
<body>
    <div class="page-wrap">
        <h1>Import Excel File into MySQL</h1>
        <p>Upload an Excel file in XLS or XLSX format and import the rows into the database.</p>

        <?php if ($successMessage !== '') { ?>
            <div class="alert success"><?= htmlspecialchars($successMessage) ?></div>
        <?php } ?>

        <?php if ($errorMessage !== '') { ?>
            <div class="alert error"><?= htmlspecialchars($errorMessage) ?></div>
        <?php } ?>

        <form action="import.php" method="post" enctype="multipart/form-data" class="import-form">
            <label for="excel_file">Choose Excel file</label>
            <input type="file" name="excel_file" id="excel_file" accept=".xls,.xlsx" required>

            <button type="submit" name="import">Import Data</button>
        </form>

        <p class="helper-text">Accepted file types: .xls, .xlsx</p>

        <p><a href="list.php">View imported records</a></p>
    </div>
</body>
</html>

This form is intentionally simple. The user selects the Excel file and submits it to import.php.

Add light CSS for a clean demo UI

The topic here is the import logic, not the design. Still, a small amount of CSS makes the demo easier to read and better for screenshots.

body {
    font-family: Arial, sans-serif;
    background: #f5f7fb;
    color: #222;
    margin: 0;
    padding: 40px 20px;
}

.page-wrap {
    max-width: 720px;
    margin: 0 auto;
    background: #fff;
    padding: 24px;
    border: 1px solid #d9e0ea;
    border-radius: 8px;
}

h1 {
    margin-top: 0;
    font-size: 28px;
}

p {
    line-height: 1.6;
}

.import-form {
    margin-top: 20px;
}

label {
    display: block;
    font-weight: 700;
    margin-bottom: 8px;
}

input[type="file"] {
    display: block;
    width: 100%;
    max-width: 100%;
    padding: 10px;
    border: 1px solid #c8d2df;
    background: #fff;
    box-sizing: border-box;
}

button {
    margin-top: 16px;
    background: #1565c0;
    color: #fff;
    border: none;
    padding: 11px 18px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 15px;
}

button:hover {
    background: #0f4fa0;
}

.alert {
    margin: 16px 0;
    padding: 12px 14px;
    border-radius: 4px;
}

.success {
    background: #edf7ed;
    color: #1b5e20;
    border: 1px solid #c8e6c9;
}

.error {
    background: #fdecec;
    color: #b71c1c;
    border: 1px solid #f5c6c6;
}

.helper-text {
    color: #555;
    font-size: 14px;
}

If you want a broader look at upload handling in PHP, this related PHPpot guide on file upload using PHP is also useful.

In the next section, we will write the import logic that validates the file, reads the spreadsheet, and inserts the rows into MySQL.

Import Excel data into MySQL using PHP

Now let us build the main import script. This file will:

  • check whether a file was uploaded
  • validate the extension
  • move the uploaded file to a safe folder
  • read the Excel sheet using PhpSpreadsheet
  • skip the first row
  • validate each data row
  • insert valid rows into MySQL using a prepared statement

Create import.php with the following code.

<?php
declare(strict_types=1);

require __DIR__ . '/config.php';
require __DIR__ . '/vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\IOFactory;

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    header('Location: index.php');
    exit;
}

if (!isset($_FILES['excel_file']) || $_FILES['excel_file']['error'] !== UPLOAD_ERR_OK) {
    header('Location: index.php?error=' . urlencode('Please choose a valid Excel file.'));
    exit;
}

$uploadedFile = $_FILES['excel_file'];
$originalName = $uploadedFile['name'] ?? '';
$tmpName = $uploadedFile['tmp_name'] ?? '';

$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$allowedExtensions = ['xls', 'xlsx'];

if (!in_array($extension, $allowedExtensions, true)) {
    header('Location: index.php?error=' . urlencode('Only XLS and XLSX files are allowed.'));
    exit;
}

$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0775, true);
}

$safeFileName = 'import_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $extension;
$destination = $uploadDir . $safeFileName;

if (!move_uploaded_file($tmpName, $destination)) {
    header('Location: index.php?error=' . urlencode('Failed to move the uploaded file.'));
    exit;
}

try {
    $reader = IOFactory::createReaderForFile($destination);
    $reader->setReadDataOnly(true);
    $spreadsheet = $reader->load($destination);
    $worksheet = $spreadsheet->getActiveSheet();
    $rows = $worksheet->toArray();
} catch (Throwable $e) {
    header('Location: index.php?error=' . urlencode('Unable to read the Excel file.'));
    exit;
}

if (count($rows) <= 1) {
    header('Location: index.php?error=' . urlencode('The Excel file does not contain data rows.'));
    exit;
}

$sql = "INSERT INTO tbl_users (name, email, phone) VALUES (?, ?, ?)";
$stmt = $mysqli->prepare($sql);

if (!$stmt) {
    header('Location: index.php?error=' . urlencode('Failed to prepare the database query.'));
    exit;
}

$importedCount = 0;
$skippedCount = 0;

foreach ($rows as $index => $row) {
    if ($index === 0) {
        continue;
    }

    $name = trim((string) ($row[0] ?? ''));
    $email = trim((string) ($row[1] ?? ''));
    $phone = trim((string) ($row[2] ?? ''));

    if ($name === '' && $email === '' && $phone === '') {
        continue;
    }

    if ($name === '' || $email === '') {
        $skippedCount++;
        continue;
    }

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $skippedCount++;
        continue;
    }

    $stmt->bind_param('sss', $name, $email, $phone);

    if ($stmt->execute()) {
        $importedCount++;
    } else {
        $skippedCount++;
    }
}

$stmt->close();

$message = "Import completed. Imported: {$importedCount}. Skipped: {$skippedCount}.";
header('Location: index.php?success=' . urlencode($message));
exit;

How this import script works

This script is small, but it does the important work in the right order.

  • It accepts only .xls and .xlsx files.
  • It stores the uploaded file with a generated filename instead of trusting the original filename.
  • It reads the sheet using IOFactory, which is the standard approach recommended in PhpSpreadsheet documentation.
  • It skips the first row because that row contains column headings.
  • It validates required values before inserting.
  • It uses a prepared statement, which is safer than building raw SQL strings.

The script uses only the active sheet. That fits the likely search intent for this topic, which is a simple and practical Excel import example.

Why prepared statements matter here

Even though the data comes from an Excel file, you should still treat it as untrusted input. A spreadsheet can contain bad values, malformed text, or unexpected characters. Prepared statements help you insert data safely and cleanly.

For a deeper explanation, see this PHPpot guide on preventing SQL injection in PHP.

PHP Excel import form showing successful import result message

Excel import success message in PHP and MySQL example

In the next section, we will display the imported database records and then cover the key security points and common errors.

Show the imported records

After importing the Excel file, it is helpful to show the inserted rows. This gives quick confirmation that the import worked as expected.

Create list.php with the following code.

<?php
declare(strict_types=1);

require __DIR__ . '/config.php';

$result = $mysqli->query("SELECT id, name, email, phone, created_at FROM tbl_users ORDER BY id DESC LIMIT 50");
?>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Imported Records</title>
    <link rel="stylesheet" href="assets/style.css">
</head>
<body>
    <div class="page-wrap">
        <h1>Imported Records</h1>
        <p><a href="index.php">Back to import form</a></p>

        <?php if ($result && $result->num_rows > 0) { ?>
            <div class="table-wrap">
                <table class="data-table">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>Name</th>
                            <th>Email</th>
                            <th>Phone</th>
                            <th>Created</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php while ($row = $result->fetch_assoc()) { ?>
                            <tr>
                                <td><?= (int) $row['id'] ?></td>
                                <td><?= htmlspecialchars($row['name']) ?></td>
                                <td><?= htmlspecialchars($row['email']) ?></td>
                                <td><?= htmlspecialchars((string) $row['phone']) ?></td>
                                <td><?= htmlspecialchars($row['created_at']) ?></td>
                            </tr>
                        <?php } ?>
                    </tbody>
                </table>
            </div>
        <?php } else { ?>
            <div class="alert error">No records found.</div>
        <?php } ?>
    </div>
</body>
</html>

Add these extra styles to the same CSS file.

.table-wrap {
    overflow-x: auto;
    margin-top: 20px;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    background: #fff;
}

.data-table th,
.data-table td {
    border: 1px solid #d9e0ea;
    padding: 10px 12px;
    text-align: left;
    vertical-align: top;
}

.data-table th {
    background: #f1f5f9;
    font-weight: 700;
}

Security considerations

This tutorial is simple, but a few security checks matter.

  • Do not trust the uploaded file type alone. Checking the extension is useful, but for stricter validation you can also inspect the MIME type and handle upload errors carefully.
  • Treat spreadsheet data as untrusted input. Excel content may still contain invalid or unexpected values. Always validate required fields before inserting into MySQL.
  • Use prepared statements. This protects the insert query from unsafe input and keeps the SQL clean.
  • Store uploads outside the public web root in production. In this demo, the upload folder is kept simple. For a real project, it is better to store uploaded files in a safer location.
  • Limit file size. Large spreadsheets can consume memory and slow down imports. Set practical upload limits for your use case.

If you need more background on safe upload handling, read the PHP manual page for handling file uploads.

Common errors and fixes

  • Composer autoload file not found
    Run composer require phpoffice/phpspreadsheet in the project folder and make sure the vendor directory exists.
  • The file uploads but nothing is imported
    Check whether the Excel sheet uses the expected column order: name, email, phone. Also confirm that the first row is only the header row.
  • Some rows are skipped
    This example skips rows with missing name, missing email, or invalid email format. That behavior is intentional.
  • Duplicate entry error for email
    The table uses a unique key on the email column. If the same email already exists, that row will fail and be counted as skipped.
  • Permission denied on uploads folder
    Make sure PHP can create and write to the uploads directory on your local server.

In the next section, we will add a short developer FAQ and then finish with the download section.

Developer FAQ

Can I import both XLS and XLSX files?

Yes. This example accepts both .xls and .xlsx files. PhpSpreadsheet can read both formats.

Can I import Excel data into another table structure?

Yes. You can change the table columns and then update the column mapping in import.php. Just make sure the Excel column order matches your PHP import logic.

Can I update existing rows instead of skipping duplicates?

Yes. In that case, you can use a different SQL approach such as INSERT ... ON DUPLICATE KEY UPDATE. That is useful when you want the Excel file to refresh existing records instead of only adding new ones.

Can I import multiple sheets from the same workbook?

Yes, but this tutorial keeps things simple and reads only the active sheet. If your workbook has multiple sheets, you can loop through them using PhpSpreadsheet and process each one based on your project rules.

What is the best library for importing Excel files in PHP?

For modern PHP projects, PhpSpreadsheet is the most practical choice. It supports reading Excel files well and is widely used.

Should I use AJAX for Excel import?

You can, but it is not required for the core task. A normal form post is easier to understand and is enough for many admin-side import tools.

Download the source code

The full working example used in this tutorial is available as a downloadable project zip.

Download the source code

The download package includes:

  • upload form
  • import script
  • database schema
  • record listing page
  • sample Excel file
  • setup instructions

If you also need the reverse flow, PHPpot has a related tutorial on exporting data to Excel in PHP.

Conclusion

Importing an Excel file into MySQL using PHP is straightforward when you break it into small steps. Upload the file, read it with PhpSpreadsheet, validate each row, and insert the cleaned data into MySQL using prepared statements.

This example keeps the flow simple, which makes it a good starting point for intermediate PHP developers. From here, you can extend it with duplicate handling, better validation, column mapping, preview before import, or background processing for large files.

Photo of Vincy, PHP developer
Written by Vincy Last updated: April 13, 2026
I'm a PHP developer with 20+ years of experience and a Master's degree in Computer Science. I build and improve production PHP systems for eCommerce, payments, webhooks, and integrations, including legacy upgrades (PHP 5/7 to PHP 8.x).

Continue Learning

These related tutorials may help you continue learning.

172 Comments on "PHP Import Excel File into MySQL Database with PhpSpreadsheet"

  • Levi says:

    how the name of the excel file can be used to create the table name and then the first row of the sheet as the Column names and import the data to this table from row 2 onwards

    • Vincy says:

      Yes, it can be done.

      Basic idea:
      use the uploaded Excel filename as the table name, read the first row as column names, create the table dynamically, and then insert data from row 2 onward.

      But do this carefully:
      • sanitize the file name before using it as a table name
      • sanitize the header values before using them as column names
      • remove spaces and special characters
      • validate everything before building the SQL

      So the flow is:
      get filename, make safe table name, read first row, make safe column names, run CREATE TABLE, then loop from the second row and insert the data.

      It is possible, but dynamic table and column creation should be used carefully in real projects.

  • Uzor says:

    Nice one …this really solved my problem..

    thanks vincy

  • Umar says:

    great tutorial, thanks alot

  • bimi says:

    Whats vendor, whats upload folder? you didn’t provide us with the most relevant information!

    • Vincy says:

      In the vendor folder you need to download the dependent and library and put it.

      • naeem says:

        how we can download, how do I know what is dependency? plz share all files with dependency, If we know the whole process then why we visit your site and get help?

      • Vincy says:

        Hi Naeem,

        I have covered this in the article. You need composer for dependency management. Its easy to do.

  • Paul says:

    everything perfect thanks! how do i select only the first sheet? Thanks a lot!

  • sanjay kumar says:

    Superb tutorial Vincy , thanks

  • Junaid Ali says:

    How can we implement the same with angular call?

    • Vincy says:

      Yes. In Angular, the Excel file is usually sent to a PHP API using FormData.

      So the flow is:
      select file in Angular, send it with HttpClient, receive it in PHP, then run the same import logic on the server.

      In short:
      • Angular handles the file upload UI
      • PHP reads the uploaded Excel file
      • PHP imports the rows into MySQL
      • Angular shows the success or error response

      So Angular is only the front end here. The actual Excel reading and database import should still happen in PHP.

  • itaIT says:

    good!….u save my day

  • Pedro says:

    This really helpful to me thank you

  • Vikram says:

    Thank You …………mam

  • Rajan says:

    but this technique doesnot work if you have to import exel files to database and bring them in web pages as a link

  • Gaurav Singh Rana says:

    Really Good Vincy, It’s Working Perfect .
    Thanks :) Keeo Going On .

  • Anderson Benjamin says:

    Thank you, it really worked.
    My only problem is; How I would import a workbook with two or more worksheets please.
    I remain thanks

  • don says:

    thank you. what should i do if i want to start at the 2nd row since the 1st row is for title such as name, description etc.

    • Vincy says:

      Hi Don,

      You need to make a minor change in the code. Declare a counter outside the loop and use that counter to skip the record of your choice.

  • Panagiotis says:

    Hi there,

    You have saved me a lot of time

  • saber says:

    Hello, I keep having this error: XMLReader::open(): Unable to open source data.

    Is there anything I can do for this to be fixed?

  • prabhat says:

    This really helpful to me thank you …mam

  • Aryan says:

    How can I check whether column name ‘Name’ & ‘Salary’ present in CSV or xlsx file or not?
    But I want without using for loop.
    Please check below code with for loop.

    $Spreadsheet = new SpreadsheetReader($fileName);
    $Spreadsheet->ChangeSheet($fileName);

    print_r($Spreadsheet);
    echo “”;
    foreach($Spreadsheet as $key=>$val) {
    if($key > 0) {
    break;
    }
    $salaryIndex = array_search(‘Salary’, $val);
    $nameIndex = array_search(‘Name’, $val);
    }

    • Vincy says:

      Hi Aryan,

      Yes. You can check only the first row and use in_array() or array_intersect() on the header row, instead of looping through all rows.

      Example idea:

      $Spreadsheet = new SpreadsheetReader($fileName);
      $Spreadsheet->ChangeSheet(0);

      $headers = $Spreadsheet->current();

      if (in_array(‘Name’, $headers) && in_array(‘Salary’, $headers)) {
      echo ‘Both columns exist’;
      } else {
      echo ‘Required columns are missing’;
      }

      So you do not need a full loop through the file. You only need to read the header row and check whether the required column names are present.

  • Applause says:

    Thanks so much. The code helps a lot

  • Lakshman B G says:

    Thanks for your knowledge sharing with open source. Your code really helps me a lot.

  • ELHADI says:

    Thank you.
    Is this scripts works for Arabic characters especially when I want to import data from Excel sheet to Mysql

    • Vincy says:

      Hi Elhadi,

      Yes, it can work with Arabic characters too.

      The important part is to use UTF-8 properly in both PHP and MySQL. In MySQL, your table and connection should use utf8mb4, and the Excel file should contain normal Unicode text.

      So if the database, table, and connection charset are set correctly, Arabic data should import fine.

  • LITA EKA says:

    how, if I want to enter 30.000 data?

    • Vincy says:

      For 30,000 rows, the main point is to avoid loading the whole sheet into memory at once.

      A better approach is:
      • read and process the file in smaller batches
      • avoid toArray() for very large files
      • use prepared statements
      • increase execution time if needed
      • add a unique key if you want to skip or update duplicates

      So yes, 30,000 rows can be imported, but the script should be optimized for large files instead of using the simple basic version as-is.

  • Khaan_iz_ here says:

    Can we convert the Excel file even if it is password protected ? So that the user converting the file won’t be able to change the contents of the excel file.

    • Vincy says:

      It depends on the type of password protection.

      If the file is only protected against editing, you may still be able to read and import it. But if the Excel file is fully encrypted with an open password, it usually needs to be unlocked first before PHP can process it.

      So yes, sometimes it is possible, but not in all cases. The safest approach is to keep the source file protected and only allow your PHP script to read and import it, without giving users direct edit access.

  • Tommi Lamanepa says:

    Thank you very much, Vincy..!! This one is what I’m looking for..!!

  • Rick says:

    Great Tutorial! Learnt a lot. Thank you.

  • Andy says:

    Permission to download, this is exactly what i needed. Thank you

  • dicky says:

    thanks vincy, you are the best

  • Beena Bhagchandani says:

    Great Tutorial! I have an excel file that contains images in a cell. how can i read the cell image.

    • Vincy says:

      Hi Beena,
      Yes, it is possible, but cell images are not read like normal text values.

      In PhpSpreadsheet, images are handled through worksheet drawing objects, not through $sheet->getCell(). So you need to loop through the sheet drawings, extract each image, save it to the server, and then map it to the related row or cell.

      So the short idea is:
      read text from cells normally, read images separately from drawings, save the image file, then store its path in MySQL if needed.

  • tesfaye says:

    It’s fantastic work,it helps me…Big Thanks

  • Val says:

    this is helpful. Thanks. Best on Internet. it works.

  • Banjo Kayode Samuel says:

    You deserve an accolade. Thanks for making my day

  • Ahmad says:

    Hello, great work!

    Please how do I replace a row if it already exist?

    • Vincy says:

      You can do that by using a unique field and changing the query to INSERT … ON DUPLICATE KEY UPDATE.

      So if a row with the same unique value already exists, MySQL will update that row instead of inserting a new one.

      Just make sure the matching column, such as email, ID, or product code, has a unique key in the table.

  • Joe says:

    Work perfectly.

  • Salman says:

    Thanks for tutorial, but how to implement that using code igniter 3??

    • Vincy says:

      Hi Salman,

      Yes, the same idea can be used in CodeIgniter 3.

      High-level flow:
      upload the Excel file using CI3 upload library, read it with PhpSpreadsheet, loop through the rows, validate the data, and then insert into MySQL using CI3 model functions.

      So mainly you will split it like this:
      • Controller for upload and import flow
      • Model for database insert
      • View for the upload form
      • PhpSpreadsheet library for reading the Excel file

      The logic stays almost the same. Only the project structure changes to match CI3 MVC.

  • pg says:

    thanks vincy..

  • Franco says:

    Hi,
    Great job, thanks a lot

  • Dee says:

    Hai. The code work perfectly fine. Thank you so much. But can i know how to import multiple excel file into different table in MySQL. with just one import button.

    • Vincy says:

      Hi Dee,

      Yes, it is possible.

      You can use one form with multiple file inputs or a multi-file upload field, then in PHP loop through each uploaded Excel file one by one. For each file, apply separate import logic and insert its data into the required MySQL table.

      So the idea is: one button, multiple files, loop through them, and map each file to its matching table before insert.

  • Jed says:

    Thanks a lot Vincy ! It’s working perfect, good job :)

  • T.S. says:

    Could You Please Give Me A Text Outline Of The Excel Sheet?

    Such As:
    ________________
    Name, Description|
    —————–|
    fds, fsd |
    —————–|
    dfsf, fdds |
    —————–|

    Thanks!

  • Samuel says:

    Fatal error: Uncaught Error: Class ‘vendor\PhpOffice\PhpSpreadsheet\Reader\Xlsx’ not found in C:\xampp\htdocs\neco\index.php:24 Stack trace: #0 {main} thrown in C:\xampp\htdocs\neco\index.php on line 24 keep getting this error while trying to import

  • Hritika says:

    Uncaught Error: Class ‘PhpOffice\PhpSpreadsheet\Reader\Xlsx’ not found.

    Hi, I am facing this error, can you please help me with this.

  • azeem says:

    how i can increase the uploading size ?

    • Vincy says:

      Hi Azeem,
      You can increase it in your PHP settings.

      Check these values in php.ini:
      • upload_max_filesize
      • post_max_size
      • max_execution_time
      • max_input_time

      Example:

      upload_max_filesize = 20M
      post_max_size = 25M
      max_execution_time = 300
      max_input_time = 300

      After changing them, restart Apache or your PHP server. Also make sure post_max_size is larger than upload_max_filesize.

  • Peacegle Mayor says:

    Thank you for this, i have one problem tho..how to import values only, not the formula?

    • Vincy says:

      Hi Mayor,

      Yes, that is possible.

      PhpSpreadsheet usually gives the calculated result of a formula cell, not the formula text itself, when you read the value normally. So to import values only, read the calculated cell value and insert that into MySQL.

      In short: do not store the formula string, store the returned cell value.

  • Bart says:

    Hi,

    While importing on Windows with xamp it works perfect. However, when i import in debian with mysql (mariadb) the same code only imports 20 records and then Problem in Importing Excel Data.
    Strange thing is thats 20 records are imported, the excel has much more rows.

    • Vincy says:

      Hi Bart,

      That usually means the import is not failing on the Excel read itself, but on one specific row after the first 20 rows are inserted.

      On Debian/MariaDB, common causes are stricter SQL settings, invalid date values, duplicate key errors, column length issues, or special characters/encoding in one row. XAMPP on Windows is often more forgiving.

      The best next step is to print or log the actual database error using $stmt->error or $mysqli->error inside the loop. That will show the exact row and reason.

      So the issue is most likely data-related or MariaDB strict-mode related, not the Excel file size itself.

  • wael says:

    I am importing file from one source and upload it into a php-msqle application similar to the one you explained above, is there anyway to get the destination application to reject the upload if the imported file has been modified or edited? i want the PHP to upload only the files that has been extracted from the source but not edited.
    thanks

    • Vincy says:

      Hi Wael,

      Yes, but PHP cannot reliably know that just by looking at the Excel file contents.

      The usual way is to add some verification from the source system, such as a checksum, hash, digital signature, or hidden token, and then validate that during upload. If the file was edited after export, the verification will fail and you can reject it.

      Without that kind of source-side validation, it is very hard to prove whether a file was modified or not.

  • Curtis says:

    Hi,

    How can I update the record with the same name, this tools have just inserted new record and cannot overwrite old values, Thanks

    • Vincy says:

      Hi Curtis,

      You can do that by making the name column unique, and then using INSERT … ON DUPLICATE KEY UPDATE instead of a plain INSERT.

      That way, if the same name already exists, MySQL will update the old row instead of adding a new one.

      Just be careful with name as the matching field, because names are not always truly unique. In most cases, using email, user ID, or another unique value is safer.

  • Vimalraj PS says:

    Thank so much Vincy.. keep posting

  • Muhammad AF says:

    Hi, may i ask, how can i select specific sheet in xlsx before import it?

    • Vincy says:

      Hi Muhammad,

      Yes. After loading the workbook, you can select the sheet by name or by index before reading the rows.

      Example idea:
      use $spreadsheet->getSheetByName(‘Sheet1’) if you know the sheet name, or $spreadsheet->getSheet(0) for the first sheet.

      Then continue the import using that selected worksheet instead of the active sheet.

  • Magut Wilson says:

    how about saving the Excel filename to mysql DB (import-template.xls)?
    any idea on how to go about it?

    • Vincy says:

      Hi Wilson,
      Yes, you can save the uploaded Excel filename to MySQL too.

      After uploading the file, get the filename from $_FILES[‘file’][‘name’] and store it in a separate column like file_name during the insert.

      If needed, you can also save the renamed server file instead of the original filename. That is usually better, because the original name may contain spaces or duplicates.

  • jissey says:

    Great tuto, thx a lot,

  • Amjad Ali says:

    Thanks its great, also working here good but not working with dates. can you help to figure it out for me.

    • Vincy says:

      Hi Amjad Ali,
      Yes, dates need a small extra conversion step.

      Excel dates often come as either formatted text or serial numbers, while MySQL expects a proper Y-m-d or Y-m-d H:i:s value. So before insert, convert the Excel date into MySQL format.

      In simple terms: read the date, convert it in PHP, then save the converted value to the database. That usually fixes the issue.

  • Pro Brah says:

    I want to check if null and assign a new value then insert the new value to Database. any thoughts and simple elaboration would do it

    • Vincy says:

      Hi Pro Brah,

      Yes. You can check the cell value in PHP before the insert, and if it is empty or null, replace it with your default value.

      Simple idea:
      read the value, test it, assign a fallback value, then insert that final value into MySQL.

      Example:

      $name = !empty($row[0]) ? $row[0] : ‘Default Name’;

      Then use $name in your insert query.
      You can do the same for any column before saving it to the database.

  • Migs says:

    Very helpful article and code. Thanks

  • John Dominic says:

    Can I use this code in my commercial project?

  • EHINJU Caleb says:

    I have a unique case here. How do I upload my excel sheet on two databases runing on separate server via Application Programming Interface (API) call? In addition, I want the a situation where if data stored in database A is updated, the changes should be reflected in database B.
    Kindly help post something on this. Thanking you always….

  • Malex says:

    I do have an excel file linked to a database, what I need to do is when someone made an update in the file automatically the database should make an update too . how can i do that . thank you

    • Vincy says:

      Hi Malex,

      Excel and MySQL do not stay synced automatically on their own.

      You need a script or scheduled job that reads the updated Excel file again and then updates the database. Usually this is done by keeping a unique key such as ID, email, or product code, and using that key to update existing rows.

      So the basic flow is: detect file change, re-import the file, and run update logic instead of plain insert.

  • samuel says:

    hi, how can i check for and skip duplicate data, when importing

    • Vincy says:

      Hi Samuel,

      You can prevent duplicates by adding a unique key in your MySQL table, for example on email, product_code, or any field that should be unique.

      Then during import, use either:
      • INSERT IGNORE to skip duplicate rows
      • INSERT … ON DUPLICATE KEY UPDATE to update existing rows

      That is the easiest and most reliable way. You can also check with a SELECT before insert, but that is slower for large imports.

  • Raj says:

    Hi Vincy

    Can you please guide how to read password protected file and how to read multiple excel files.

    • Vincy says:

      Hi Raj,

      You can read multiple Excel files by looping through the uploaded files one by one and processing each file with the same import logic.

      For password-protected Excel files, it is a bit different. PhpSpreadsheet has only limited support depending on the file type, and many protected files may need to be unlocked first before importing.

      So for multiple files, yes, it is straightforward. For password-protected files, it depends on how the file is protected and may need a separate handling approach.

  • PRIYA TIWARI says:

    hey , vincy can you give me any idea about dynmaic sql insert in php . suppose if I don’t want to specify column name and it’s count and I also do not want to specify table name

    • Vincy says:

      Hi Priya,

      You can do it dynamically, but you still need to know the table name and the column list at runtime from some source, such as the Excel header row or user input.

      The usual approach is:
      read the table name, fetch the column names, build the column list and placeholders in PHP, and then execute a prepared statement.

      So yes, dynamic insert is possible, but it should be done carefully. Never pass raw table or column names directly without validation, because that can break the query or create security issues.

  • fred says:

    the vendor folder is empty why?

  • Sentoso Huang says:

    It’s work perfect! Thank you @Vincy

  • Adarsh says:

    Suppose a new column is added in excel sheet then how can i dynamically/automatically make changes in the database without importing the excel sheet again and again…

    • Vincy says:

      Hi Adarsh,
      Yes, it is possible, but it should be handled carefully.

      First read the Excel header row and compare it with your database columns. If a new column is found, you can add it using an ALTER TABLE query before importing the rows.

      So the basic idea is:
      read header names from Excel, check existing table fields, add missing columns, then continue the import.

      But fully automatic database changes can be risky in a live project. It is safer to validate the new column names and allowed data types before altering the table.

  • Aditya says:

    Awesome tutorial. Quick question though. Does this work for any version of php?

  • Ceres says:

    Can I use this in my live project?

  • Sheena says:

    Thanks for the project download

  • valkis says:

    Good morning, nice work.

  • Emre says:

    Beautiful article

  • Naxay says:

    Thanks for this helpful article.

  • Rao says:

    Vincy

    getting error like…
    Fatal error: Uncaught Error: Class ‘PhpOffice\PhpSpreadsheet\Reader\Xlsx’ not found

    all the files are in vendor folder and i have autoload.php file in vendor folder.

    Can you please help.

  • Aberam Wijayakumar says:

    Wow! what an article. Better than any found in the Net. God be with you.

  • shubham says:

    Thanks for the excellent example.

  • Aniket says:

    Hello mam i want to keep in excel file image and text both to upload database through excelsheet using php mysql.
    if is this possible, give me outline idea.

    • Vincy says:

      Yes, it is possible.

      Text can be imported from cells as usual. Images can also be extracted from the Excel sheet, saved into a server folder, and then only the image filename or path can be stored in MySQL.

      The main challenge is correctly matching each image to the right row. So it is possible, but it needs some custom handling beyond a normal Excel import.

      Usually, this is done using PhpSpreadsheet.
      The Excel file is read first for text data.
      Then the embedded images are extracted separately.
      Those image files are saved on the server.
      After that, the saved image path is linked to the matching database row.

  • Ayman says:

    My excel sheet has too many records and I am getting an error message because of the memory. I added it ini_set(‘memory_limit’, ‘-1’); which loads all the records (15000 rows) but I get a message Problem in Importing Excel Data. any solution? Alternatively, can we have script to update the file (void duplicates) and add rows that do not exist in sql table?

    • Vincy says:

      For 15,000 rows, the problem is usually not just PHP memory limit. The script may also hit execution time, inefficient row loading, or duplicate-key failures during insert.

      A few things usually help:
      1. Do not use ini_set(‘memory_limit’, ‘-1’) as the real fix. That only removes the limit and can make the server unstable on large files.
      2. Read the sheet in a lighter way and process row by row instead of loading everything into a big array with toArray().
      3. Increase max_execution_time if needed, because large imports can take time.
      4. Add proper error logging so you can see the real database or spreadsheet error instead of only showing Problem in Importing Excel Data.
      5. Use a unique key in MySQL, such as email, product code, or invoice number, and then import with either:
      • INSERT IGNORE to skip duplicates
      • INSERT … ON DUPLICATE KEY UPDATE to update existing rows

      So yes, you can absolutely make the script avoid duplicates and insert only new rows, or update existing rows if they already exist.

  • mario says:

    Excellent example project, thanks.

  • Ayman M says:

    My date in the excel file is formatted as such:
    DatePurchased
    1/27/2023
    4/17/2023

    When I import the excel, I get the following in my SQL columns 0000-00-00 00:00:00 for the date. Any suggestions to fix it?

    • Vincy says:

      That usually happens because Excel dates are often read as either formatted strings or Excel serial date numbers, and MySQL expects a proper Y-m-d H:i:s value.

      In your case, values like 1/27/2023 should be converted before insert. If you are reading the cell with PhpSpreadsheet, first check whether it is an Excel date value, then convert it to PHP date format.

      If you insert the raw Excel value directly into a DATETIME or DATE column, MySQL may store 0000-00-00 00:00:00 because it does not recognize the incoming format correctly.

      So the fix is: convert the Excel date to MySQL format before executing the insert query.

  • Keita says:

    hi, i have downloaded the PHPSpreadsheet on GitHub by clicking provided link. But i cannot find the autoload.php file. Please help with the vendor folder and its dependencies.

    • Vincy says:

      Hi Keita,

      I have used Composer for PHPSpreadSheet. If you want to put the downloaded zip file, you need make a minor change to the project code where I use the autoload

  • Sanjay says:

    Ihave included vedor folder but getting following errors.

    Warning: require_once(vendor/autoload.php): Failed to open stream: No such file or directory in C:\xampp\htdocs\Projects\import-excel-to-mysql\index.php on line 8

    • Vincy says:

      Hi Sanjay,

      You need to have Composer and do “composer require phpoffice/phpspreadsheet” to get the autoload

  • Shay says:

    Vincy. You’re a legend!

  • Edward says:

    Hi Vincy,

    I get the error as follow… “Warning: require_once(vendor/autoload.php): Failed to open stream: No such file or directory in H:\xampp\htdocs\import_excel\index.php on line 8”

  • Henry says:

    Hi

    Thank you. Simple and best tutorial. Easy to use example

Leave a Reply

Your email address will not be published. Required fields are marked *

Explore topics
Need PHP help?