【问题标题】:php get data from csv and again write them to the same csvphp 从 csv 获取数据并再次将它们写入相同的 csv
【发布时间】:2016-09-30 09:56:45
【问题描述】:

我有一个这样的 csv

product_id,stock_unit
1, ,
2, ,
3, ,
4, ,
5, ,
6, ,

现在,在我的 php 代码中,我将获取产品 ID 并对数据库运行查询以从数据库中获取库存单位。在获取产品的库存单位后,脚本应将其更新为 csv,而不更改任何名称。

所以我的代码是这样的

<?php
$mysqli = new mysqli("localhost", "username", "password", "database");

/* check connection */
if ($mysqli->connect_errno) {
    printf("Connect failed: %s\n", $mysqli->connect_error);
    exit();
}

$file = "update_pd.csv";
$csv = array_map("str_getcsv", file($file,FILE_SKIP_EMPTY_LINES));
$keys = array_shift($csv);

$stock_array = array();
if (($handle = fopen($file, "r")) !== FALSE) {
    fgetcsv($handle);
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        $stock_array[] = get_data($data[0]);
    }
    fclose($handle);
}
//print_r($stock_array);
$handle = fopen("update_pd.csv", "a");
fputcsv($handle, $stock_array);


function get_data($id) {
    if( !empty($id) ) {
        $result = $mysqli->query("SELECT `stock_unit` FROM `products` WHERE `product_id` = ".$id." ");
        if( $result ) {
            while($row = $result->fetch_assoc()) {
                return $row['stock_unit'];
            }
        }
    }
}

这里是过度写入 csv 我的意思是数据在最后一行写入,所有旧数据都存在。该脚本应根据 csv 文件中的产品 id 写入库存数量,而不更改 csv 标题和文件名。那么有人可以告诉我该怎么做吗?任何帮助和建议都将是非常可观的。

更新

因为库存单位将从数据库中获取。输出应该是这样的

product_id,stock_unit
1, 24,
2, 22,
3, 37,
4, 46,
5, 89,
6, 96,

【问题讨论】:

  • 你到底为什么要使用 CSV? mysql 做同样的事情,但堆得更好。
  • @e4c5 我的要求是这样的;)
  • 我不明白你在做什么。请提供输出样本。另外,请说明您是要追加还是覆盖。
  • @Gordon 我只想获取那些产品 id 已在 csv 中提及的产品的库存单位,然后再次将库存单位写入同一个 csv。库存单位当前在数据库表中
  • 所以你想覆盖旧的 csv?对吗?

标签: php mysql csv fputcsv


【解决方案1】:

如果我的理解正确,您的代码可以正常工作,但会附加数据而不是覆盖文件。这是因为你在做:

$handle = fopen("update_pd.csv", "a");

这将以追加模式打开文件。要覆盖它,您需要以写入模式打开它,例如

$handle = fopen("update_pd.csv", "w");

参见PHP Manual for fopenmode参数的说明

'w' - 只为写入而打开;将文件指针放在文件的开头并将文件截断为零长度。如果文件不存在,请尝试创建它。

'a' - 只为写入而打开;将文件指针放在文件末尾。如果该文件不存在,请尝试创建它。在这种模式下,fseek() 没有任何作用,写入总是追加。

但是,您在 get_data 函数中使用了 $mysqli 变量,但该变量未在该范围内定义,因此它根本不应该工作。确保将变量传递给函数,例如

function get_data($mysqli, $id) {

然后这样称呼它:

get_data($mysqli, $data[0]);

但如果您有一个基本相同的查询,并且只在WHERE 子句中使用不同的值,则使用Prepared Statements 将获得更快的查询执行速度。

在创建 mysqli 实例后在全局范围内定义它:

$query = $mysqli->prepare(
    "SELECT `stock_unit` FROM `products` WHERE `product_id` = ?
);

然后,不要将$mysqli 变量传递给get_data,而是传递$query 变量。把get_data函数改成这段代码:

function get_data($query, $id) {
    if( !empty($id) ) {
        $query->bind_param("i", $id);
        $result = $query->execute($query);
        …

作为替代方案,不要单独获取库存单位,而是考虑这样做:

$file = "file.csv";
$data = array_map('str_getcsv', file($file, FILE_SKIP_EMPTY_LINES));
$ids = array_filter(array_column($data, 0), 'is_numeric');
$ids = implode(',', $ids);

然后使用WHERE … ININTO OUTFILE 在一个查询中完成所有操作:

SELECT "product_id", "stock_unit"
UNION ALL
SELECT product_id, stock_unit
FROM products
WHERE product_id IN ($ids)
INTO OUTFILE '/path/to/update_pd.csv'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY ',\n';

请注意,由于存在 SQL 注入攻击的风险,将变量插入查询是一种不好的做法。但是由于我们确保 id 是数字的,所以在这种情况下没关系。另外,mysqli does not allow easy binding of values in WHERE … IN queries

【讨论】:

  • 如果不进行这样的查询我的意思是没有 OUTFILE 之类的东西,还有其他选择吗?
  • @NewUser 是的,您当然可以继续这样做,就像您正在这样做一样,只需更改 fopen 模式即可。见更新。但是,在这种情况下,我建议将您的 SELECT 查询更改为使用准备好的语句,因为这将提高您的数据库调用的执行速度。
  • 您能否展示一些示例代码如何做到这一点。我只是不想像你在 OUTFILE 中那样从查询中写入文件
  • @NewUser 有什么理由你不想一口气完成吗?这种方式效率更高
猜你喜欢
  • 2019-12-13
  • 2018-10-11
  • 2015-06-26
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多