簡體   English   中英

PHP腳本在將數據從CSV導入MySQL時保持超時

[英]PHP script keeps timing out while importing data from CSV to MySQL

我很難過這個。 我在PHP / MySQL的初學者水平以上,在這個網站上發布很新。 GoDaddy把我轉到一個網格服務器來提高性能,並通過我編寫腳本的方式解決問題。 它從CSV中抓取數據並嘗試插入規范化數據庫。

CSV未規范化,因此需要檢查是否存在某些內容。 我最初打開/關閉結果集,但后來建議我使用准備好的語句,不幸的是我遇到了同樣的問題。 在獲得廣泛的“內部服務器錯誤”之前,我可以通過大約1200條14k記錄。 日志中的錯誤引用了一個安全功能,可以防止在短時間內過多地訪問FastCGI服務器。

我正在努力尋找和學習的是完成我想要做的事情的正確方法 - 檢查是否存在某些東西; 如果是,請獲取記錄ID。 如果沒有,請插入數據並獲取新ID。 我的代碼如下。 它從簡單的php文件上傳表單中獲取文件名和隱藏屬性,然后從那里開始。 這只會由我使用,我插入的數據是公共記錄,因此安全性不是主要問題。

<?php

if ($_POST["upload"] == "1") {

    //Connect to the database
    $hostname = xxx;
    $username = xxx;
    $dbname = xxx;
    $password = xxx;

    $dbh = mysqli_connect($hostname,$username,$password,$dbname) or die("Problem connecting: ".mysqli_error());

    $stmt = mysqli_stmt_init($dbh);


    //check for file errors
  if ($_FILES["file"]["error"] > 0)
    { echo "Return Code: " . $_FILES["file"]["error"] . "<br>"; }
  //No file errors
  else
    {

    //If file already exists
    if (file_exists($_FILES["file"]["name"]))
    { 
            echo $_FILES["file"]["name"] . " already exists.";
            exit; 
    }

    //If it doesn't exist
    else
      {
      move_uploaded_file($_FILES["file"]["tmp_name"],
      $_FILES["file"]["name"]);
      echo "Stored in: " . $_FILES["file"]["name"] . "<br><br>";
      $strFileName = $_FILES["file"]["name"];
      }
    }

    //File reporting
    echo "Upload: " . $_FILES["file"]["name"] . "<br>";
    echo "Type: " . $_FILES["file"]["type"] . "<br>";
    echo "Size: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
    echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br>";



$row = 0;
if (($handle = fopen($strFileName, "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        $num = count($data);
        $row++;
        $strPermitNo = trim($data[0]);

        //Check to see if the permit is already in the database
        $sql = "SELECT LocID FROM tbl_TABC_Locations WHERE LocPermitNo = ?";

        if (mysqli_stmt_prepare($stmt, $sql))
        {
            mysqli_stmt_bind_param($stmt, "s", $strPermitNo);
            mysqli_stmt_bind_result($stmt, $intLocID);
            mysqli_stmt_execute($stmt);

            $strPermitResult = 0;

            while (mysqli_stmt_fetch($stmt))
            {
                $strPermitResult = $intLocID;
            }
        }

        //If no permits, insert it
        if ($strPermitResult == "0")
        {       
            //Clean Location name
            $strLocName = trim($data[1]);
            $strLocName = str_replace('"', "", $strLocName);
            $strLocName = str_replace(";","-", $strLocName);
            $strLocName = addslashes($strLocName);

            $strInsertQuery = "INSERT INTO tbl_TABC_Locations (LocName,LocAddress,LocCity,LocState,LocZip,LocCounty,LocPhone,LocPermitNo) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";

            if (mysqli_stmt_prepare($stmt, $strInsertQuery)) 
            {
              mysqli_stmt_bind_param($stmt, 'ssssiiis', $field1, $field2, $field3, $field4, $field5, $field6, $field7, $field8);

              $field1 = $strLocName;
              $field2 = trim(addslashes($data[2]));
              $field3 = trim(addslashes($data[3]));
              $field4 = trim($data[4]);
              $field5 = trim($data[5]);
              $field6 = trim($data[6]);
              $field7 = trim($data[7]);
              $field8 = $strPermitNo;
              mysqli_stmt_execute($stmt);

              $intLocID = mysqli_insert_id($dbh);
            }
        }

        else 
        {
            $intLocID = $strPermitResult;
        }


        //Report dates
        $strReportDate = trim($data[8]);
        $aryNewDate = explode("/", $strReportDate);
        $strNewYear = $aryNewDate[0];
        $strNewMonth = $aryNewDate[1];

        //Check to see if the report date is already in there
        $sql = "SELECT ReportDateID FROM tbl_TABC_ReportDates WHERE ReportYear = ? AND ReportMonth = ?";

        if (mysqli_stmt_prepare($stmt, $sql))
        {
            mysqli_stmt_bind_param($stmt, "ii", $strNewYear, $strNewMonth);
            mysqli_stmt_bind_result($stmt, $intReportDateID);
            mysqli_stmt_execute($stmt);

            $strReportDateResult = 0;

            while (mysqli_stmt_fetch($stmt)) 
            {
                $strReportDateResult = $intReportDateID;
            }
        }   

        if ($strReportDateResult == "0")
        {
            $strInsertQuery = "INSERT INTO tbl_TABC_ReportDates (ReportMonth,ReportYear) VALUES (?, ?)";

            if (mysqli_stmt_prepare($stmt, $strInsertQuery)) 
            {
              mysqli_stmt_bind_param($stmt, "ii", $field1, $field2);

              $field1 = $strNewMonth;
              $field2 = $strNewYear;

              mysqli_stmt_execute($stmt);

              $intDateID = mysqli_insert_id($dbh);
            }
        }
        else
        {
            $intReportDateID = $strReportDateResult;
        }


        //Check to see if they have reported for the month already, and if not, add the report      
        $sql = "SELECT ReportID FROM tbl_TABC_Reports WHERE ReportDateID = ? AND LocID = ?";    

        if (mysqli_stmt_prepare($stmt, $sql))
        {
            mysqli_stmt_bind_param($stmt, "ii", $intReportDateID, $intLocID);
            mysqli_stmt_bind_result($stmt, $intReportID);
            mysqli_stmt_execute($stmt);

            $strReportIDResult = 0;

            while (mysqli_stmt_fetch($stmt)) 
            {
                $strReportIDResult = $intReportID;
            }
        }   


        if ($strReportIDResult == "0")
        {

            $strInsertQuery = "INSERT INTO tbl_TABC_Reports (LocID,ReportDateID,TaxReceipts) VALUES (?, ?, ?)";

            if (mysqli_stmt_prepare($stmt, $strInsertQuery)) 
            {
              mysqli_stmt_bind_param($stmt, "iid", $field1, $field2, $field3);

              $field1 = $intLocID;
              $field2 = $intReportDateID;
              $field3 = trim($data[9]);

              mysqli_stmt_execute($stmt);

              echo "New report<br>\n";
            }
        }
        else { echo "<b>Already reported</b><br>"; }

    }
    echo "Closing file now";
    fclose($handle);
}

mysqli_close($dbh);
}

日志中的錯誤是這樣的:

[2436594] [fcgid:warn](104)通過對等方重置連接:[client xxx] mod_fcgid:從FastCGI服務器,referer(我的網頁地址)讀取數據時出錯

[fcgid:warn](104)連接由對等方重置:[client xxx] mod_fcgid:ap_pass_brigade在handle_request_ipc函數,referer(我的網頁地址)中失敗

編輯12/15(在循環之外拉出准備好的語句)。 現在我仍然得到“准備語句中的變量數量不匹配”錯誤:

$sql1 = "SELECT LocID FROM tbl_TABC_Locations WHERE LocPermitNo = ?";
$ps_ChkPermit = mysqli_stmt_prepare($stmt, $sql1);

if ($ps_ChkPermit)
{
mysqli_stmt_bind_param($stmt, "s", $strPermitNo);
mysqli_stmt_bind_result($stmt, $intLocID);
mysqli_stmt_execute($stmt);
...
}

通常,您希望從循環內移除盡可能多的計算到循環外部。 因為在循環的每次迭代期間,每行代碼都會反復執行。

@MikeW建議您盡可能減少腳本的資源需求。 請考慮以下示例(未經測試的代碼!):

while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {

    $sql = "SELECT LocID FROM tbl_TABC_Locations WHERE LocPermitNo = ?";

    if (mysqli_stmt_prepare($stmt, $sql)) // <-- this keeps running every time.
    {
        mysqli_stmt_bind_param($stmt, "s", $strPermitNo);
        mysqli_stmt_bind_result($stmt, $intLocID);
        mysqli_stmt_execute($stmt);
        ...
    }
}

為什么每次都要反復准備SQL語句?

$sql = "SELECT LocID FROM tbl_TABC_Locations WHERE LocPermitNo = ?";

// this statement gets prepped outside the loop and runs only once.
$prepared_statement = mysqli_stmt_prepare($stmt, $sql);

// loop starts...
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {

    if ($prepared_statement)
    {
        // and then you simply bind the params and execute from within the loop.
        mysqli_stmt_bind_param($stmt, "s", $strPermitNo);
        mysqli_stmt_bind_result($stmt, $intLocID);
        mysqli_stmt_execute($stmt);
        ...
    }
}

這樣,您可以節省資源,尤其是在必須處理CSV中的多行時。

當然,這意味着您需要為不同的查詢使用不同的變量名稱,以便您可以識別每個查詢。 在您當前聲明$stmt位置執行此操作

他的建議的第二部分涉及更多的工作。 要減少查詢數,可以在數據庫中創建一個包含YEAR和MONTH的新字段,並將其設置為mysql中的UNIQUE索引。 這樣,如果您嘗試插入現有記錄,mysql將拋出並出錯。

如果插入時出錯,您可以假設您有該日期的報告。 如果您沒有錯誤,則報告是新的。

那么你沒有額外的步驟准備另一個查詢只是為了檢查報告的存在!

正如我在上面指出的那樣,您還可以減少CSV文件的大小,因此完成時間不會太長。

繼@ halfer建議之后,使用PHP-CLI運行此腳本可能更簡單。 沒有內存限制超時 - 但這意味着您需要將上傳的文件保存在某處並使用cron任務以便稍后處理它們...

需要熟悉命令行:)

希望這清楚一點...祝你好運!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM