【问题标题】:Right way of validating xlsx files before database inserting在插入数据库之前验证 xlsx 文件的正确方法
【发布时间】:2017-09-08 10:43:18
【问题描述】:

在玩 PHPExcel 时,我遇到了一些问题,如何正确处理验证/将值插入数据库。我不需要任何代码,只需要大致的概念即可。

首先,我遍历第一行以检查列是否与给定的列匹配(如果它符合架构)。

在下一步中,我得到行,同时它的beeing 验证行/列明智。如果类型不匹配我会得到一个错误。

在验证行时,我需要获取 Worker 名称并将其转换为 id get_worker_id()

问题编号 #1。 这种解决方案是一个好习惯吗?它将产生多达 100 个查询。 Foreach 行 - 1。

问题编号 #2 我还需要再次验证行,我将使用 worker_id、F 和 G 列来检查数据库中是否不存在此类记录。我将简单地介绍一个类似于get_worker_id() 的函数,但如果条目存在,它将返回真/假。

但这又是正确的做法吗?通过原始计算,我的方法将产生 100 次选择(get_worker_id)、100 次选择(验证是否存在)、100 次插入(如果一切正常)。

我不确定我是否做得正确。你能给我一些建议吗?

感谢转发。

处理 xlsx 文件的模型。

class Gratyfikant_model extends CI_Model {

    private $_limit = 100;

    const columns = array(
        'A' => "Z",
        'B' => "KS",
        'C' => "G",
        'D' => "S",
        'E' => "Numer",
        'F' => "Miesiąc", // required
        'G' => "Data wypłaty", // required
        'H' => "Pracownik", // required
        'I' => "Brutto duże", // required
        'J' => "ZUS pracownik", // required
        'K' => "ZUS pracodawca", // required
        'L' => "Do wypłaty", // required 
        'M' => "Obciążenie", // required
        'N' => "FW");
    const validators = array(
        'F' => 'date',
        'G' => 'date',
        'H' => 'string',
        'I' => 'float',
        'J' => 'float',
        'K' => 'float',
        'L' => 'float',
        'M' => 'float',
    );
    const validators_errors = array(
        'float' => "Wartość nie jest liczbą",
        'string' => "Wartość nie jest poprawna",
        'date' => "Wartość nie jest datą"
    );

    protected $_required = array(
        'H', 'I', 'J', 'K', 'L', 'M'
    );
    private $_sheet = array();
    private $_sheet_pracownicy = array();
    private $_agregacja = array();
    protected $_invalid_rows = array();

    public function __construct() {
        parent::__construct();
    }

    public function read_data(array $dane) {
        if (count($dane) > $this->_limit) {
            throw new Exception('Limit wierszy to ' . $this->_limit);
        }
        $this->_sheet = $dane;
        return $this;
    }

    public function column_validation() {
        foreach ($this->_required as $r) {
            if (!isset($this->_sheet[1][$r]) || $this->_sheet[1][$r] != self::columns[$r] || !array_key_exists($r, $this->_sheet[1])
            ) {
                throw new Exception('Kolumna - ' . $r . ' - Wartość nagłówka nie pasuje do szablonu, powinno być ' . self::columns[$r]);
            }
        }

        return $this;
    }

    function validateDate($date) {
        $d = DateTime::createFromFormat('Y-m-d', $date);
        return $d && $d->format('Y-m-d') === $date;
    }

    private function row_validation($k, $a, $v, $f) {

        switch ($v) {
            case "date":
                $cellval = $this->validateDate(PHPExcel_Style_NumberFormat::toFormattedString($f, PHPExcel_Style_NumberFormat::FORMAT_DATE_YMD));
                break;
            case "float":
                $cellval = is_float($f);
                break;
            case "string":
                $cellval = is_string($f);
                break;
            default:
                break;
        }
        if (!$cellval) {
            $this->_invalid_rows[$a][$k] = $v;
        }
    }

    public function get_sheet_data() {
        $dane = $this->_sheet;
        unset($dane[1]); // remove first col

        $zus_pracownik = 0;
        $zus_pracodawca = 0;
        $zus_lacznie = 0;
        $do_wyplaty = 0;
        $obciazenie = 0;
        $brutto = 0;
        foreach ($dane as $a => $d) {
            foreach (self::validators as $k => $v) {
                echo $this->row_validation($k, $a, $v, $d[$k]);
            }
            if (!is_null($d["H"]) && !empty($d["H"])) {
                // $this->_sheet_pracownicy[$d["H"]]["numer"] = PHPExcel_Style_NumberFormat::toFormattedString($d["E"], PHPExcel_Style_NumberFormat::FORMAT_DATE_DDMMYYYY);
                $this->_sheet_pracownicy[] = array(
                    "pracownik" => $d["H"],
                    "miesiac" => PHPExcel_Style_NumberFormat::toFormattedString($d["F"], PHPExcel_Style_NumberFormat::FORMAT_DATE_YMD),
                    "data_wyplaty" => PHPExcel_Style_NumberFormat::toFormattedString($d["G"], PHPExcel_Style_NumberFormat::FORMAT_DATE_YMD),
                    "zus_pracownik" => $d["J"],
                    "zus_pracodawca" => $d["K"],
                    "zus_lacznie" => bcadd($d["K"], $d["J"]),
                    "do_wyplaty" => $d["L"],
                    "obciazenie" => $d["M"],
                    "brutto" => $d["I"],
                    "id_prac" => $this->get_worker_id($d["H"]));

                $zus_pracownik = bcadd($zus_pracownik, $d["J"]);
                $zus_pracodawca = bcadd($zus_pracodawca, $d["K"]);
                $zus_lacznie = bcadd($zus_lacznie, bcadd($d["K"], $d["J"]));
                $do_wyplaty = bcadd($do_wyplaty, $d["L"]);
                $obciazenie = bcadd($obciazenie, $d["M"]);
                $brutto = bcadd($brutto, $d["I"]);
            }
        }
        $this->_agregacja = array(
            "zus_pracownik" => $zus_pracownik,
            "zus_pracodawca" => $zus_pracodawca,
            "zus_lacznie" => $zus_lacznie,
            "do_wyplaty" => $do_wyplaty,
            "obciazenie" => $obciazenie,
            "brutto" => $brutto
        );

        return $this;
    }

    public function display_result() {
        if (empty($this->_invalid_rows)) {
            return array(
                "wartosci" => $this->_sheet_pracownicy,
                "agregacja" => $this->_agregacja
            );
        }
    }

    public function display_errors() {
        foreach ($this->_invalid_rows as $k => $a) {
            foreach ($a as $key => $value) {
                throw new Exception('Pole ' . $key . '' . $k . ' ' . self::validators_errors[$value]);
            }
        }
        return $this;
    }

    public function get_worker_id($getAd) {

        $this->db->select('id_pracownika as id')
                ->from('pracownicy')
                ->like('CONCAT( imie,  \' \', nazwisko )', $getAd)
                ->or_like('CONCAT( nazwisko,  \' \', imie )', $getAd);


        $query = $this->db->get();



        $result = $query->result_array();
        if (isset($result[0]["id"])) {
            return $result[0]["id"];
        } else {
            throw new Exception('Nie odnaleziono ' . $getAd . ' w bazie danych, proszę dodać pracownika a następnie ponownie wczytać plik');
        }
    }

}

显示

 try {

            $data['s'] = $this->gm
                    ->read_data($sheetData)
                    ->column_validation()
                    ->get_sheet_data()
                    ->display_errors()
                    ->display_result();


        } catch (Exception $e) {

            $data['ex'] = $e->getMessage();
        }

XLSX 文件示例

+---+---------------+---+---+------------+---------+--------------+-----------+-------------+---------------+----------------+------------+------------+--------+
| Z |      KS       | G | S |   Numer    | Miesiąc | Data wypłaty | Pracownik | Brutto duże | ZUS pracownik | ZUS pracodawca | Do wypłaty | Obciążenie |   FW   |
+---+---------------+---+---+------------+---------+--------------+-----------+-------------+---------------+----------------+------------+------------+--------+
|   | nieprzekazany | G |   | 03.08.2017 | sie.17  |   08.09.2017 | Worker1   |        2000 |         274,2 |          392,2 |    1459,48 |     2392,2 | (brak) |
|   | nieprzekazany | G |   | 03.08.2017 | sie.17  |   08.09.2017 | Worker2   |        1000 |         137,1 |          171,6 |     768,24 |     1171,6 | (brak) |
|   | nieprzekazany | G |   | 03.08.2017 | sie.17  |   08.09.2017 | Worker3   |        2000 |         274,2 |          392,2 |    1413,88 |     2392,2 | (brak) |
|   | nieprzekazany | G |   | 03.08.2017 | sie.17  |   08.09.2017 | Worker4   |        2000 |         274,2 |          392,2 |    1418,88 |     2392,2 | (brak) |
+---+---------------+---+---+------------+---------+--------------+-----------+-------------+---------------+----------------+------------+------------+--------+

【问题讨论】:

    标签: php mysql phpexcel


    【解决方案1】:

    这实际上取决于您的应用程序的规模以及此 Excel 文件的导入频率。例如,如果您的应用程序几乎没有收到流量,那么每行运行多个查询并不是世界末日。如果您已经设置并运行了服务器和数据库,那么您不妨利用它们。相反,如果您的应用程序一直处于高负载状态,那么尽量减少您运行的查询量可能是个好主意。

    选项 1

    如果您的应用程序很小和/或没有获得太多流量,那么不必担心您需要进行约 300 次查询。 MySQL 并不脆弱,如果您已经很好地索引了您的数据,您的查询将会非常快。

    选项 2

    首先查询您需要的数据并将其存储在内存中,以便您可以在 PHP 中执行逻辑检查。

    这意味着对于问题 1,您应该在一个查询中获取所有工作人员,然后在 PHP 中构建一个查找数组。

    这是一个非常粗略的例子:

    // Get all workers
    SELECT worker_name, worker_id FROM workers;
    
    // Build a lookup array from the query results
    $worker_array = array(
       'Worker1' => 1,
       'Worker2' => 2,
       ...
    );
    
    // Then as you loop each row check if the work is in your lookup array
    if ( ! isset($worker_array[$excel_row['worker_name']])) {
       // do something
    }
    

    同样,对于问题 2,您可以在一次查询中获得唯一的数据样本(您不需要整条记录,只需要唯一的字段)。但是,如果您有很多独特的数据样本,这可能会出现问题。

    选项 3

    在 MySQL 中创建一个临时表并导入您的 Excel 数据而不执行任何逻辑检查。然后您可以完全在 SQL 中执行逻辑检查。

    这是一个非常粗略的例子,对你的数据结构一无所知:

    -- Get all records in the Excel data that match unique data samples
    SELECT
      *
    FROM
      temporary_table tt
    JOIN
      workers w
      ON w.worker_name=tt.worker_name
    JOIN
      data d
      ON d.worker_id=w.worker_id
      AND d.col_f=tt.col_f
      AND d.col_g=tt.col_g
    

    如果数据没有问题,那么您可以从临时表执行 INSERT 到数据表中。这将您的查询限制为初始插入(您也可以批量处理以获得更好的性能)、数据检查以及从临时数据到真实数据的插入。

    回顾

    这一切都取决于您的应用程序。如果您可以摆脱执行选项 1 并且您已经实现了它,那么现在就可以了。如果您没有看到此应用程序疯狂增长,则无需过度优化。

    但是,如果您担心规模和增长,那么我个人会考虑实施选项 3。

    【讨论】:

      【解决方案2】:

      这里有多个问题:

      将导入拆分为多个阶段。

      1. 验证标头。 (如果发现错误则中断导入)2)迭代 每一行。
      2. 验证行。
      3. 如果有效则导入。
      4. 如果有日志错误。
      5. 如果所有行都已处理,则停止处理文件或返回到 2。

      至于天气,您需要一些分块,这取决于您的脚本消耗了多少时间和内存。如果你需要它,它就像读取 X 行到内存然后处理它一样简单。在极端情况下,您可以单独加载每条记录。 如果您不需要它,只需将其全部加载到数组中。

      chunking - 在一次迭代中最多消耗 X 行,然后清除内存然后消耗下一个块...

      【讨论】:

        【解决方案3】:

        对于单个 php 实例,100 个查询对我来说并不合适。

        https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/

        或者只搜索n加1查询问题

        【讨论】:

          猜你喜欢
          • 2015-10-25
          • 2019-04-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-06-04
          相关资源
          最近更新 更多