isaacdew/load-data

使用 Laravel 的 LOAD DATA INFILE 在 MySQL 中加载大型 CSV 文件的流畅语法。

0.1.1 2024-03-16 22:20 UTC

This package is auto-updated.

Last update: 2024-09-16 23:28:53 UTC


README

MySQL 和 MariaDB 内置了 LOAD DATA INFILE 语句,该语句允许将大型数据集从 CSV 或类似文件快速加载到表中。此包提供了在 Laravel 中构建和执行 LOAD DATA INFILE 语句的 API。

安装

使用 composer 安装此包

composer require isaacdew/load-data

示例

基本示例

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->fieldsTerminatedBy(',')
    ->fieldsEnclosedBy('"', optionally: true)
    ->load();

忽略 CSV 头部

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->ignoreHeader()
    ->load();

定义列

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->fieldsTerminatedBy(',')
    ->fieldsEnclosedBy('"', optionally: true)
    ->columns([
        'column_one',
        'column_two',
        'column_three'
    ])
    ->load();

使用 CSV 的头部定义您的列

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->fieldsTerminatedBy(',')
    ->fieldsEnclosedBy('"', optionally: true)
    ->useFileHeaderForColumns()
    ->load();

默认情况下,CSV 头部会被转换为蛇形命名,因为列名需要与数据库表中的列名匹配。如果您需要进行任何自定义修改,可以传递一个回调给 useFileHeaderForColumns 方法。

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->fieldsTerminatedBy(',')
    ->fieldsEnclosedBy('"', optionally: true)
    // Remove parenthesis from column names
    ->useFileHeaderForColumns(fn($column) => preg_replace('/(\(.*)$/', '', $column))
    ->load();

仅从 CSV 加载特定列

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->fieldsTerminatedBy(',')
    ->fieldsEnclosedBy('"', optionally: true)
    ->useFileHeaderForColumns()
    ->onlyColumns([
        'column_one',
        'column_two'
    ])
    ->load();

在加载前截断表

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->truncateBeforeLoad()
    ->load();

使用 SET 语句

要使用此功能,您必须首先使用 columns 方法或使用文件头部通过 useFileHeaderForColumns 方法定义列。然后您可以使用 MySQL 表达式修改 CSV 中的值。一个好的用例是日期列,CSV 中没有使用 MySQL 便于格式的日期。请注意,您必须使用 @ 前缀来在表达式中使用列名。

use Isaacdew\LoadData\LoadData;

LoadData::from(storage_path('path/to/file.csv'))
    ->to('tablename')
    ->fieldsTerminatedBy(',')
    ->fieldsEnclosedBy('"', optionally: true)
    ->columns([
        'column_one',
        'column_two',
        'column_three',
        'date_column'
    ])
    ->setColumn('date_column', "STR_TO_DATE(@date_column, '%c/%d/%Y')") // Convert MM/DD/YYYY to a MySQL date
    ->load();

专用数据库服务器

如果您的 Laravel 应用程序不在与数据库相同的服务器上,您必须确保数据库服务器和 PDO 中启用了 LOAD DATA LOCAL INFILE 语句。此包将在 DB_HOST 设置为 127.0.0.1localhost 之外时自动使用 LOCAL 关键字。

要启用 PDO 中的 LOAD DATA LOCAL INFILE,请转到您的 config/database.php 文件,并在 mysql 连接的选项数组中添加 PDO::MYSQL_ATTR_LOCAL_INFILE => true,如下所示

return [
    'connections' => [
        //...
        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
                PDO::MYSQL_ATTR_LOCAL_INFILE => true // Add this line!
            ]) : [],
        ],
        //...
    ]
];

关于安全性的说明

对于 LOAD DATA INFILE 语句,不支持预处理语句。在这种情况下,不要使用用户输入来构建 LOAD DATA INFILE 语句。我已经采取了预防措施使用 PDO::quote() 来转义文件名,但我仍然建议不要在此语句中使用用户提供的文件名。