【问题标题】:parse XML and insert to DB if exists otherwise UPDATE如果存在,则解析 XML 并插入到 DB 更新
【发布时间】:2018-01-02 09:51:48
【问题描述】:

我正在解析一个 XML 文件,如果 id 不存在,我想 INSERT XML 元素节点,或者如果存在则 UPDATE 记录...

到目前为止,这是我的代码:

<?php
header('Content-type: text/html; charset=UTF-8') ;
//connection to DB here..
//.... .. .. .. ..


// Create connection
$conn = new mysqli($servername, $dbuser, $password, $dbname,3306);
// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
// Change character set to utf8
$conn->set_charset("utf8");
date_default_timezone_set('Europe/Athens');

$date_modified = strtotime("now");
$business_id = 54;
$xml_link = "https://www.mydomain.gr/test.xml";
$xml_link = $conn->real_escape_string($xml_link);
$xml_link = trim(stripslashes($xml_link));

这里我正在更新记录“date_modified”..

$update_business_xml = $conn->query('UPDATE business_xml SET date_modified="' . $date_modified . '" WHERE business_id=54');

现在,在这部分代码中,我获取了产品的所有 id,并将它们放入一个数组中

$count_errors = 0;
//query to find products ids
$query_ids = $conn->query('SELECT pid FROM products WHERE business_id=54');
$rows_ids = mysqli_num_rows($query_ids);
$count_id = 0;
if($rows_ids > 0) {
    while($exe_ids = mysqli_fetch_object($query_ids)) {
        $arr_ids[$count_id] = $exe_ids->pid;
        $count_id++;
    }
}

$reader = new XMLReader();
$reader->open($xml_link);           
while($reader->read()) {
    if($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'product' ) {
        $product = new SimpleXMLElement($reader->readOuterXml());

        $pid = $product->id;
        $name = $product->name;
        $name = mb_strtolower($name,'UTF-8');
        $mpn = $product->mpn;
        $ean = $product->ean;
        $sku = $product->sku;
        $link = $product->link;
        $price = $product->price;
        $category_id =  $product->category->attributes();
        $category_path = $product->category;
        $category_path = mb_strtolower($category_path,'UTF-8');
        $image = $product->image;
        $availability = $product->availability;
        $size = $product->size;
        $size = mb_strtolower($size,'UTF-8');
        $color = $product->color;
        $color = mb_strtolower($color,'UTF-8');
        $weight = $product->weight;
        $description = $product->description;
        $manufacturer = $product->manufacturer;
        $manufacturer = trim($manufacturer);
        $instock = "Y";
        $product_image = $image;
        $check_product_url = $link;

        $exist_pids = 0;
        if(!empty($pid) || !empty($image) || !empty($price) || !empty($name) || !empty($link) || !empty($manufacturer)) {
            if($category_id == 613 || $category_id == 604 || $category_id == 635) {

我在 XML 中有一些类别,我不希望它们在我的数据库中,所以我在这里获取类别的属性,并检查此产品是否是其中之一,只需将此记录的状态更新到我的数据库.

                $update_business_xml = $conn->query('UPDATE products SET status=0 WHERE business_id="' . $business_id . '" AND pid= "' . $pid . '"');
                $count_errors++;
            }
            else {
                $status = 1;
                $date = date('d-m-Y H:i:s'); //when insert a pr
                $date_modified = strtotime("now"); //when modify a pr
                $insert_business_xml = $conn->query('INSERT INTO products (business_id,pid,name,category,product_link,price,size,color,weight,description,manufacturer,mpn,ean,image,sku,instock,availability,status,date_added) VALUES("'.$business_id.'", 
                "' . mysqli_real_escape_string($conn,stripslashes($pid)) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes($name)) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim($category_path))) . '", 
                "' . $check_product_url . '", 
                "' . mysqli_real_escape_string($conn,stripslashes($price)) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($size)))) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($color)))) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim($weight))) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim($description))) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($manufacturer)))) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim($mpn))) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim($ean))) . '", 
                "' . $product_image . '", 
                "' . mysqli_real_escape_string($conn,stripslashes(trim($sku))) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes($instock)) . '", 
                "' . mysqli_real_escape_string($conn,stripslashes($availability)) . '", 
                "' . $status . '", "' . $date . '") ON DUPLICATE KEY UPDATE
                "business_id='.$business_id.'", 
                "pid=' . mysqli_real_escape_string($conn,stripslashes($pid)) . '", 
                "name=' . mysqli_real_escape_string($conn,stripslashes($name)) . '", 
                "category=' . mysqli_real_escape_string($conn,stripslashes(trim($category_path))) . '", 
                "product_link=' . $check_product_url . '", 
                "price=' . mysqli_real_escape_string($conn,stripslashes($price)) . '", 
                "size=' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($size)))) . '", 
                "color=' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($color)))) . '", 
                "weight=' . mysqli_real_escape_string($conn,stripslashes(trim($weight))) . '", 
                "description=' . mysqli_real_escape_string($conn,stripslashes(trim($description))) . '", 
                "manufacturer=' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($manufacturer)))) . '", 
                "mpn=' . mysqli_real_escape_string($conn,stripslashes(trim($mpn))) . '", 
                "ean=' . mysqli_real_escape_string($conn,stripslashes(trim($ean))) . '", 
                "image=' . $product_image . '", 
                "sku=' . mysqli_real_escape_string($conn,stripslashes(trim($sku))) . '", 
                "instock=' . mysqli_real_escape_string($conn,stripslashes($instock)) . '", 
                "availability=' . mysqli_real_escape_string($conn,stripslashes($availability)) . '", 
                "status=' . $status . '", 
                "date_modified=' . $date_modified . '"');

在上面的代码中,我使用 INSERT INTO ... ON DUPLICATE KEY UPDATE 来检查 id 是否存在,只需更新这条记录的所有值。如果 id 不存在,插入这个。 . 正如我所说,在我的代码开始时,我得到了一个数组中的所有 id,所以我在这里擦除已找到的 id..

                //erase from arr_ids
                if(($key = array_search($pid, $arr_ids)) !== false) {
                    unset($arr_ids[$key]);
                }
            }
        }
        else {
            $update_business_xml = $conn->query('UPDATE products SET status=0, date_modified="' . $date_modified . '" WHERE business_id="' . $business_id . '" AND pid= "' . $pid . '"');
            $count_errors++;
        }
    } //reader nodeType
} //end while loop
$reader->close();

最后但并非最不重要的一点是,在这里我还使用数组中剩下的所有其余 id 更新记录..这意味着这些 id 不存在或未在 XML 中列出任何更多。

foreach($arr_ids as $id) {
    $update_business_xml = $conn->query('UPDATE products SET status=0 WHERE business_id="' . $business_id . '" AND pid= "' . $id . '"');
}

最后,我有一个简单的消息,以便查看有多少错误。我所说的错误是指 XML 的多少记录具有空元素(名称、价格、链接、图像.. .. 等)并发出回声..

//var_dump($arr_ids);
$insert_messages = "Your XML file has been updated successfully! We found <strong>" . $count_errors . "</strong> errors. In case errors found, please check your dashboard!";
echo $insert_messages;
$conn->close();
?>

所有这些代码都在一个 php 文件中,作为 cronjob 运行! 现在的问题和我的问题是,我没有看到 UPDATE 运行正确或根本没有运行,因为我的数据库中的 date_modified 始终为 NULL,这是初始化值。我错过了什么? (如果这里有多个错误,通常我该怎么做才能修复整个代码?)

提前致谢

带有一种产品的 XML 示例(希腊 XML)

<mystore>
<created_at>2017-07-26 16:01:20</created_at>
<products>
<product>
<id>9307</id>
<name>
<![CDATA[ Minimum ανδρικό t-shirt φλάμα Percy ivory ]]>
</name>
<link>
<![CDATA[
https://www.mydomain.gr/andrika-rouxa/tshirts-andrikes-mployzes/minimum-andriko-t-shirt-percy-ivory.html
]]>
</link>
<image>
<![CDATA[
https://www.mydomain.gr/images/detailed/51/minimum-andriko-t-shirt-percy-122690105_(1).jpg
]]>
</image>
<sku>
<![CDATA[ 122690105-wh ]]>
</sku>
<mpn>
<![CDATA[ 122690105-wh ]]>
</mpn>
<category id="30">
<![CDATA[ ΑΝΔΡΙΚΑ > T- shirts ]]>
</category>
<price>27.30</price>
<description>
<![CDATA[
<ul><li>χρώμα ελεφαντόδοντου</li><li>στρογγυλή λαιμόκοψη</li><li>στρογγυλεμένο και μακρύτερο πίσω μέρος</li><li>regular fit</li><li>100% cotton</li></ul>
]]>
</description>
<instock>Y</instock>
<availability>Σε απόθεμα</availability>
<manufacturer>
<![CDATA[ Minimum ]]>
</manufacturer>
<size>L,XL</size>
<sex>
<![CDATA[ Άνδρας ]]>
</sex>
</product>
</products>
</mystore>

date_modified 在我的数据库中是 varchar(128) - utf8_general_ci 我不认为这是一个问题,对吧?

【问题讨论】:

  • @Parfait 我已经编辑了我的问题.. 感谢您花时间回复..
  • 哪个UPDATE 不起作用?循环之前的第一个还是带有INSERT 的那个?请解释什么是有效的。除了 date_modified 之外的记录是否会追加和更新?如果该字段是您声称的varchar,那么即使您传入一个整数strtotime(),第一次更新也不会失败,因为MySQL 将相应地进行转换。
  • 请注意您的 INSERT 没有触及 date_modified,只有 date_added。因此,对于 date_ modified,这些记录将是 NULL。仔细检查您的数据并报告具体问题。
  • INSERT 适用于 XML 具有但我没有(在我的数据库中)的新产品..这就是为什么我使用 $date 我想在列 date_added 中插入.. 另一方面,如果产品 id 存在,我想通过更新列 date_modified 来更新它。现在我想做什么更清楚了吗?
  • 我知道你在做什么,但不知道什么是有效的?哪个UPDATE 查询是问题所在? INSERT 有效吗?

标签: php xml xml-parsing sql-update sql-insert


【解决方案1】:

考虑使用 products_temp 表,products 的精确结构,但仅用于存储 XML 数据。从那里运行所需的追加和更新查询以从临时表迁移到最终表。

如果这里有任何要点,请使用parameterized queries,它可以避免引号括起来和变量连接,以获得更简洁、更安全、可维护的代码。

XML 数据处理(在循环内追加查询)

// CLEAN OUT TEMP TABLE
$sql = 'DELETE FROM products_temp';
$delete_xml = $conn->query($sql);

// PREPARED STATEMENT
$sql = 'INSERT INTO products_temp (business_id, pid, `name`, `category`, product_link, price,
                                   size, color, weight, `description`, manufacturer, mpn, ean,
                                   image, sku, instock, availability, `status`, date_added) 
        VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';

// APPEND ALL RAW XML DATA INTO TEMP TABLE (IN LOOP, WITHOUT INNER IF LOGIC)
//...same xml objects

while($reader->read()) {
    if($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'product') {
        //...same xml variables
        $product = new SimpleXMLElement($reader->readOuterXml());
        $stmt = $conn->prepare($sql);

        $stmt->bind_param("sssssssssssssssssss", 
                          mysqli_real_escape_string($conn,stripslashes($pid)), 
                          mysqli_real_escape_string($conn,stripslashes($name)), 
                          mysqli_real_escape_string($conn,stripslashes(trim($category_path))), 
                          $check_product_url, 
                          mysqli_real_escape_string($conn,stripslashes($price)), 
                          mysqli_real_escape_string($conn,stripslashes(trim(strtolower($size)))), 
                          mysqli_real_escape_string($conn,stripslashes(trim(strtolower($color)))), 
                          mysqli_real_escape_string($conn,stripslashes(trim($weight))), 
                          mysqli_real_escape_string($conn,stripslashes(trim($description))), 
                          mysqli_real_escape_string($conn,stripslashes(trim(strtolower($manufacturer)))), 
                          mysqli_real_escape_string($conn,stripslashes(trim($mpn))), 
                          mysqli_real_escape_string($conn,stripslashes(trim($ean))), 
                          $product_image, 
                          mysqli_real_escape_string($conn,stripslashes(trim($sku))), 
                          mysqli_real_escape_string($conn,stripslashes($instock)), 
                          mysqli_real_escape_string($conn,stripslashes($availability)), 
                          $status, 
                          $date);

        $stmt->execute();
    }
}

临时数据处理(在循环外,每个调用一次,避免使用数组和其他foreach

使用WHERE NOT EXISTS

// APPEND ONLY NEW TEMP PRODUCTS WITH RELEVANT INFO AND NOT IN SPECIAL CATEGS INTO PRODUCTS
$sql = 'INSERT INTO products (business_id, pid, `name`, `categor`y, product_link, price,
                              size, color, weight, `description`, manufacturer, mpn, ean,
                              image, sku, instock, availability, `status`, date_added) 
        SELECT t.business_id, t.pid, t.name, t.category, t.product_link, t.price,
               t.size, t.color, t.weight, t.description, t.manufacturer, t.mpn, t.ean,
               t.image, t.sku, t.instock, t.availability, t.status, t.date_added
        FROM products_temp t
        WHERE NOT EXISTS (SELECT 1 FROM products sub 
                          WHERE sub.p_id = t.p_id AND sub.business_id = t.business_id)
          AND t.image IS NOT NULL AND t.price IS NOT NULL AND t.name IS NOT NULL 
          AND t.link IS NOT NULL AND t.manufacturer IS NOT NULL
          AND t.category_id NOT IN (604, 613, 635)';
$insert_business_xml = $conn->query($sql);

使用UPDATE INNER JOIN

// UPDATE MATCHED TEMP PROUCTS WITH MISSING RELEVANT INFO OR IN SPECIAL CATEGS (I.E., ERRORS)
$sql = 'UPDATE products p INNER JOIN products_temp t
                           ON p.p_id = t.p_id AND p.business_id = t.business_id
        SET p.status=0, p.date_modified = ?
        WHERE t.image IS NULL OR t.price IS NULL OR t.name IS NULL 
           OR t.link IS NULL OR t.manufacturer IS NULL
           OR t.category IN (604, 613, 635)';

$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $date_modified);

$stmt->execute();
$count_errors = $mysqli->affected_rows;     // ERRORS FOR MESSAGE AT END


// UPDATE EXISTING MATCHED TEMP PRODUCTS WITH RELEVANT INFO AND NOT IN SPECIAL CATEGS
$sql = 'UPDATE products p INNER JOIN products_temp t
                           ON p.p_id = t.p_id AND p.business_id = t.business_id
        SET p.business_id = t.business_id, p.name = t.name, p.category = t.category, 
            p.product_link = t.product_link, p.price = t.price, p.size = t.size, 
            p.color = t.color, p.weight = t.weight, p.description = t.description, 
            p.manufacturer = t.manufacturer, p.mpne = t.mpn, p.ean = t.ean,
            p.image = t.image, p.sku = t.sku, p.instock = t.instock, 
            p.availability = t.availability, p.status = t.status, p.date_added = t.date_added
        WHERE t.image IS NOT NULL AND t.price IS NOT NULL AND t.name IS NOT NULL 
          AND t.link IS NOT NULL AND t.manufacturer IS NOT NULL
          AND t.category_id NOT IN (604, 613, 635)';
$update_business_xml = $conn->query($sql);


// UPDATE EXISTING NON-MATCHED TEMP PRODUCTS
$sql = 'UPDATE products p SET p.status = 0
        WHERE NOT EXISTS (SELECT 1 FROM products_temp sub 
                          WHERE sub.p_id = p.p_id AND sub.business_id = p.business_id)';
$update_business_xml = $conn->query($sql);

注意:当然,如果没有实际数据和数据库,这一切都是未经测试的。请调整任何被忽略的语法并集成到更大的代码库中。

【讨论】:

  • 我想我明白了准备和“ssssss”的工作原理。你在mysql查询中使用问号..?,?,?... 之后你使用prepare ->($sql ) 和 bind_param("s",$date_modified);您就像用变量替换 s 一样,对吗?但是仍然有一些问题......我收到了这个错误:“italic Number of elements in type definition string doesn't match number of bind variables italic” in this line你说的代码: $stmt = $conn->prepare($sql); $stmt->bind_param("s", $date_modified); $stmt->执行(); $count_errors = $mysqli->affected_rows;
  • 如果我有一个“s”和一个“?”在mysql里面我应该有一个变量($date_modified),对吧?
  • 正确。请阅读大量关于 php/mysqli 参数化的离线/在线文档和教程。是的,? 是占位符,s 定义 string 类型(使用 i 表示 integer 类型)。至于错误,您在追加查询中传递了 19 个值,我数了 17 个,所以我又添加了两个问号和 s。
  • 好的,我做了我的测试并且在某些情况下工作。当 XML 文件不够大时,UPDATES & INSERTS 工作。但是当 XML 文件很大时,我收到 500 个内部服务器错误.. 和 PHP 警告消息(在日志文件中)[2017 年 7 月 29 日 11:15:29 UTC] PHP 警告:mysqli::mysqli(): (42000/1226): 用户 '*****' 已超过第 16 行 /path/to/my/cronjob/file/cronjob.php 中的 'max_user_connections' 资源(当前值:30) .. .. . . 我应该为此更改我的 php.ini 文件吗?对于所有这些插入然后更新,这不是一个很快的好主意,对吗?我猜.. ..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-16
  • 2015-03-31
  • 1970-01-01
  • 2023-03-17
  • 2020-09-01
  • 2014-10-23
相关资源
最近更新 更多