【问题标题】:CSV file upload to handle status update & inserting new recordsCSV 文件上传以处理状态更新和插入新记录
【发布时间】:2018-02-09 05:11:45
【问题描述】:

在处理本地托管的项目时,我一直忙于管理 CSV 上传。其中一项任务要求我每天上传具有新条目或现有条目更新状态的数据。也有可能某些条目(存在于数据库中)没有更新状态。

问题陈述;

我创建了一个 CSV 上传功能,可将 CSV 文件上传到特定位置,并将信息导入指定的 TABLE 中。 我想知道在进行 CSV 上传时验证数据库记录的最佳方式是什么。

理想情况下它应该如下工作;

  1. 如果条目不存在(插入来自 CSV 文件的新条目基础数据)
  2. 如果条目存在并且状态与新上传的 CSV 文件相同(IGNORE & 什么也不做)
  3. 如果条目存在且状态与新上传的 CSV 文件中的条目不同(更新状态为 CSV 文件中提及的条目)

数据库/CSV文件结构

  • tracking_id(自动递增)
  • odanumber(通过 CSV 上传,可以有重复的条目)
  • 空运单(通过 CSV 和 UNIQUE 上传)
  • courierful(通过 CSV 上传,可能有重复条目)
  • delstatus(通过 CSV 上传,主要更新内容)
  • deliverydate(通过 CSV 上传并在每次交付时更新)

从上面可以看出,几乎每次(对于现有条目)上传新的 CSV 时,delstatus 都会更新,因此需要检查。

我假设我们可以选择“airwaybill”来检查它是否存在,并且 如果是,请检查 delstatus 是否与 CSV 文件相同或 更新。如果 'airwaybill' 不存在,则必须添加新记录 到数据库。因为这样可以避免我输入所有记录 不必要的数据库。或者可以以更好的方式完成(即 我还没有探索)。


现在发生了什么;

我可以上传完整的 CSV 文件,通过以下代码在数据库中创建新条目。

<?php 

if(isset($_POST['csv']))
{
$sqlname= 'localhost';
$username= 'root';
$table= 'tracking';
$password= '';
$db='aatrack';
$file=$_POST['csv'];
$cons= mysqli_connect("$sqlname", "$username","$password","$db") or die(mysql_error());

$result1=mysqli_query($cons,"select count(*) count from $table");
$r1=mysqli_fetch_array($result1);
$count1=(int)$r1['count'];


mysqli_query($cons, '
    LOAD DATA LOCAL INFILE "'.$file.'"
        INTO TABLE '.$table.'
        FIELDS TERMINATED by \',\'
        LINES TERMINATED BY \'\n\'
        IGNORE 1 LINES
')or die(mysql_error());

$result2=mysqli_query($cons,"select count(*) count from $table");
$r2=mysqli_fetch_array($result2);
$count2=(int)$r2['count'];

$count=$count2-$count1;
if($count>0)
{
    header("location:success.php?id=$count");
}

}

?>

您能否帮助指导实现相同目标的最佳方法。我知道这可以通过首先将信息上传到 temp_table 并在 LIVE 表中更新条目之前进行比较来完成。

请提出实现结果的最佳方法。

感谢您阅读本文。

最好的问候,

阿米特·阿格尼霍特里

【问题讨论】:

  • 假设每一行都有一个可用的唯一键/复合键,INSERT ... ON DUPLICATE KEY UPDATE ... 对你有用吗? dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html
  • 如果该条目在 csv 中不存在,您会: 1) 将其单独留在数据库中; 2)从数据库中删除它; 3) 更新数据库中的状态?
  • @WeeZel 如果 CSV 中不存在该条目,我会将其单独留在数据库中。谢谢!
  • @Weezel 你对我有同样的建议吗?
  • @AmitAgnihotri 我(仍然)确定 LOAD INFILE 是您的最佳解决方案。我读过,否则使用其他方法导入需要很长时间。现在, LOAD INFILE 在进行记录更新时的行为是:删除旧记录并插入具有更新值的新记录。这意味着每次设置一个新的 tracking_id 值,如果它不是从 csv 文件中读取的。那么,我的第一个问题:您是否还从 csv 文件中导入“tracking_id”值?

标签: php mysql csv file-upload load-data-infile


【解决方案1】:

LOAD DATA INFILE 的工作原理

基于 UNIQUE 索引,LOAD DATA INFILE 插入新记录或更新现有记录(仅当 REPLACE 选项处于活动状态时)。

(1)关于插入

如果在 db 表中未找到 UNIQUE 索引列的 csv 输入值,则添加一条新记录,其中包含来自 csv 文件的(定义的)输入值。

(2)关于更新

如果在 db 表中找到 UNIQUE 索引列的 csv 输入值,则 LOAD DATA INIFILE 查询执行以下操作(按此顺序!):

  • 插入新的 csv 值作为具有新 PRIMARY KEY id 的新记录;
  • 删除数据库中的旧记录。

注意:在我的其余回答中,我将只谈论更新部分 (2)。

BEFORE INSERT-TRIGGER 作为条件更新的解决方案

由于 LOAD DATA INFILE 在删除操作之前运行插入操作,因此您可以利用 在插入具有 csv 值的新记录时仍然存在旧 db 记录这一事实。因此,您可以根据旧记录中包含的值自定义新输入值。真正很酷的部分是:您甚至可以维护 PRIMARY KEY 字段的旧值

关键是定义一个BEFORE INSERT-TRIGGER,所有需要的自定义、验证和分配都驻留在其中:

  • 通过运行 SELECT sql 语句获取旧记录的值;
  • 将获取的值存储到之前定义的用户变量中;
  • 使用用户变量比较旧值与 csv 输入值;
  • 基于此比较:指定 PRIMARY KEY 字段的旧值作为新值,并将新的 csv 值更改为旧值或其他值,如果也需要。

然后从 PHP 执行 LOAD DATA INFILE 查询。

代码

创建表语法:

CREATE TABLE `tracking` (
  `tracking_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `odanumber` int(11) DEFAULT NULL,
  `airwaybill` int(11) DEFAULT NULL,
  `courierful` varchar(100) DEFAULT NULL,
  `delstatus` tinyint(1) DEFAULT NULL,
  `deliverydate` varchar(19) DEFAULT NULL,
  PRIMARY KEY (`tracking_id`),
  UNIQUE KEY `uni_airwaybill` (`airwaybill`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

在插入触发之前:

USE `tests`;

DELIMITER $$

DROP TRIGGER IF EXISTS tests.tracking_BEFORE_INSERT$$
USE `tests`$$
CREATE DEFINER = CURRENT_USER TRIGGER `tests`.`tracking_BEFORE_INSERT` BEFORE INSERT ON `tracking` FOR EACH ROW
BEGIN

    /* Define vars to store old record values. */
    SET @old_tracking_id = NULL;
    SET @old_odanumber = NULL;
    SET @old_courierful = NULL;
    SET @old_delstatus = NULL;
    SET @old_deliverydate = NULL;

    /* 
        Fetch the existing record if exists and pass 
        its values into the correspnding vars.
    */
    SELECT 
        tracking_id,
        odanumber,
        courierful,
        delstatus,
        deliverydate 
    INTO 
        @old_tracking_id,
        @old_odanumber,
        @old_courierful,
        @old_delstatus,
        @old_deliverydate 
    FROM tracking 
    WHERE airwaybill = NEW.airwaybill
    LIMIT 1;

    /* If an old record was found... */
    IF @old_tracking_id IS NOT NULL THEN

        /* ...set the new record's tracking_id to it. */
        SET NEW.tracking_id = @old_tracking_id;

        /* ...and if delstatus are the same... */
        IF NEW.delstatus = @old_delstatus THEN

            /* ...maintain the old record values. */
            SET NEW.odanumber = @old_odanumber;
            SET NEW.courierful = @old_courierful;
            SET NEW.deliverydate = @old_deliverydate;

        END IF;

    END IF;

END$$
DELIMITER ;

CSV 文件 (tracking.csv)

odanumber,airwaybill,"courierful",delstatus,"deliverydate"
19,1,abc,0,2017-04-31
25,2,def,1,2017-05-31
103,3,ghi,1,2017-06-31
324,4,jkl,1,2017-07-31
564,5,mno,0,2017-08-31

LOAD DATA INFILE 函数(从 PHP 调用)

LOAD DATA INFILE "<PATH-TO>/tracking.csv"
REPLACE
INTO TABLE tests.tracking
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(odanumber, airwaybill, courierful, delstatus, deliverydate);

注意事项:

*) 关于 LOAD DATA INFILE,可能是您遇到了错误:

ERROR 1290 (HY000): MySQL 服务器正在运行 --secure-file-priv 选项,所以它不能执行这个语句

这意味着: LOAD DATA INFILE 没有读取 csv 文件的权限。所以你必须自己在你的数据库(my.cnf,或my.ini)的配置文件中设置secure-file-priv。像这样:

[mysqld]
secure-file-priv = "<PATH-TO-FOLDER-CONTAINING-THE-CSV-FILES>/"

*) 您不能定义从中运行 LOAD DATA INFILE 的存储过程。

最后,还有其他涉及临时表的解决方案,毫无疑问,它们可以完美运行。其中之一在this great article 中介绍。因此,触发解决方案只是另一种方法。

祝你好运!

【讨论】:

  • 你能用我的回答解决问题吗?我真的很想从你那里找到这个。
【解决方案2】:

这里有两种情况:

  1. 表格的列与 csv 列完全匹配。在这种情况下,REPLACE 就是答案 - 它是LOAD DATA INFILE 的关键字,请参阅doc entry

  2. 表的列与 csv 列不匹配:REPLACE 会导致 冲突 记录被删除并重新插入,从而有效地删除了附加数据。在这种情况下LOAD DATA INFILE 本身无效,您需要另一种方法,之前过滤您的文件,通过 php 或其他方法进行更新。

无论如何,如果您想在导入过程中添加更多“逻辑”,LOAD DATA INFIlE 可能并不是真正正确的方法,但使用临时表很可能会从数据库提供的所有优点中受益。

【讨论】:

    猜你喜欢
    • 2013-02-06
    • 1970-01-01
    • 2022-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-13
    相关资源
    最近更新 更多