在数据库插入之前验证xlsx文件的正确方法

时间:2017-09-08 10:43:19

标签: php mysql phpexcel

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

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

在下一步中,我得到了行,同时它的行验证了行/列。如果类型不匹配,我将收到错误。

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

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

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

但这又是一种正确的做法吗?通过原始计算,我的方法将产生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) |
+---+---------------+---+---+------------+---------+--------------+-----------+-------------+---------------+----------------+------------+------------+--------+

3 个答案:

答案 0 :(得分:2)

这实际上取决于应用程序的规模以及导入此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。

答案 1 :(得分:2)

这里有许多问题:

将导入分为几个阶段。

  1. 验证标头。 (如果发现错误,则中断导入)2)迭代 在每一行。
  2. 验证行。
  3. 导入(如果有效)。
  4. 记录错误(如果有)。
  5. 如果处理完所有行或者返回2,则停止处理文件。
  6. 至于天气,你需要一些分块,这取决于你的脚本消耗多少时间和内存。如果您需要它,就像将X行读取到内存然后处理它一样简单。极端情况下,您可以单独加载每条记录。 如果你不需要它,只需将它全部加载到数组中。

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

答案 2 :(得分:1)

对于单个php实例,100个查询对我来说听起来不对。

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

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