非常慢的循环PHP
Posted
技术标签:
【中文标题】非常慢的循环PHP【英文标题】:Very slow loop PHP 【发布时间】:2022-01-22 09:02:18 【问题描述】:我是一名初级开发人员,我遇到了脚本循环问题。 循环循环大数组来自数据库。 问题是在最短的时间内完成循环是可能的,但目前,大约 500 个元素需要 15 分钟才能完成。 这是不可接受的。 我的文件类型需要引号中的空格。 这是私有类函数中的代码:
$length = count($this->fileH1);
for ($z = 0; $z < $length; $z++)
$this->fileH2[$z]['id_paziente'] = $this->fileH1[$z]->id_paziente;
$this->fileH2[$z]['regione'] = decifra($_SESSION['cod_regione']);
$this->fileH2[$z]['asl'] = decifra($_SESSION['cod_asl']);
$this->fileH2[$z]['cod_struttura'] = decifra($_SESSION['cod_struttura']);
$this->fileH2[$z]['tipo_assist'] = "RIA";
$this->fileH2[$z]['tipo_strutt'] = decifra($_SESSION['tipo_struttura']);
// CERCO LE INFO DELLE MENOMAZIONI DEL PAZIENTE
$stmt_get_info_menomazioni = $this->centro->prepare('SELECT codice, icd9_nuovo FROM tbl_pazienti_terapie_menomazioni WHERE id_paziente = ? AND id_contratto = ? LIMIT 1');
$stmt_get_info_menomazioni->bind_param("ii", $this->fileH1[$z]->id_paziente, $this->fileH1[$z]->id_contratto); //$fileH1[$z]['id_terapia']);
$stmt_get_info_menomazioni->execute();
$stmt_get_info_menomazioni->store_result();
$stmt_get_info_menomazioni->bind_result($cod_menomazione, $icd9_menomazione);
if ($stmt_get_info_menomazioni->num_rows > 0)
$stmt_get_info_menomazioni->fetch();
if ($cod_menomazione !== NULL || $cod_menomazione !== '')
$this->fileH2[$z]['cod_menomazione'] = $cod_menomazione;
else $this->fileH2[$z]['cod_menomazione'] = ' ';
if ($icd9_menomazione !== NULL || $icd9_menomazione !== '')
$this->fileH2[$z]['icd9_menomazione'] = str_pad($icd9_menomazione, 10, " ");
else $this->fileH2[$z]['icd9_menomazione'] = ' ';
else
$this->fileH2[$z]['cod_menomazione'] = ' ';
$this->fileH2[$z]['icd9_menomazione'] = ' ';
$stmt_get_info_menomazioni->close();
$this->fileH2[$z]['num_registro'] = $this->fileH1[$z]->anno_rif . decifra($_SESSION['cod_asl']) . '0' . date('y') . str_pad($_POST['mese'], 2, '0', STR_PAD_LEFT) . '0001';; // "203021040001"; // AGGIUNGERE numero registro struttura
$this->fileH2[$z]['medico_autorizz'] = ' ';
$this->fileH2[$z]['cod_medico_autorizz'] = ' ';
$this->fileH2[$z]['istat_primo_ricovero'] = '000000';
$this->fileH2[$z]['progressivo'] = $this->fileH1[$z]->progressivo;
// CERCO LE INFO DELLE TERAPIE DEL PAZIENTE
$stmt_get_info_menomazioni = $this->centro->prepare('SELECT data_autorizz, data_inizio, data_fine FROM tbl_pazienti_contratti WHERE id_paziente = ? AND id = ? LIMIT 1');
$stmt_get_info_menomazioni->bind_param("ii", $this->fileH1[$z]->id_paziente, $this->fileH1[$z]->id_contratto); //$fileH1[$z]['id_terapia']);
$stmt_get_info_menomazioni->execute();
$stmt_get_info_menomazioni->store_result();
$stmt_get_info_menomazioni->bind_result($data_autorizz, $data_inizio, $data_fine);
if ($stmt_get_info_menomazioni->num_rows > 0)
$stmt_get_info_menomazioni->fetch();
if ($data_autorizz !== NULL || $data_autorizz !== '0000-00-00') $this->fileH2[$z]['data_prescrizione'] = date('dmY', strtotime($data_autorizz)); else $this->fileH2[$z]['data_prescrizione'] = ' ';
if ($data_inizio !== NULL || $data_inizio !== '0000-00-00') $this->fileH2[$z]['data_inizio_terapia'] = date('dmY', strtotime($data_inizio)); else $this->fileH2[$z]['data_inizio_terapia'] = ' ';
if ($data_fine !== NULL || $data_fine !== '0000-00-00') $this->fileH2[$z]['data_fine_terapia'] = date('dmY', strtotime($data_fine)); else $this->fileH2[$z]['data_fine_terapia'] = ' ';
// CALCOLO LE DATE DEL CICLO DI FATTURAZIONE
$data_inizio_mese_attuale = date('Y-m-1');
$data_inizio_terapia = date('Y-m-d', strtotime($data_inizio));
if ($data_inizio_mese_attuale < $data_inizio_terapia)
$this->fileH2[$z]['data_inizio_periodo_fatturazione'] = date('dmY', strtotime($data_inizio_terapia));
else $this->fileH2[$z]['data_inizio_periodo_fatturazione'] = date('dmY', strtotime($data_inizio_mese_attuale));
$data_fine_mese_attuale = date('Y-m-t'); // t = ultimo gg del mese attuale
$data_fine_terapia = date('Y-m-d', strtotime($data_fine));
if ($data_fine_mese_attuale > $data_fine_terapia)
$this->fileH2[$z]['data_fine_periodo_fatturazione'] = date('dmY', strtotime($data_fine_terapia));
else $this->fileH2[$z]['data_fine_periodo_fatturazione'] = date('dmY', strtotime($data_fine_mese_attuale));
// CALCOLO QTA PRESTAZIONI EFFETTUATE
$stmt_get_qta_prestaz_eff = $this->centro->prepare('SELECT COUNT(id) FROM tbl_pazienti_terapie_presenze WHERE MONTH(DATE(ingresso_effettuato)) = ? AND id_paziente = ?');
$stmt_get_qta_prestaz_eff->bind_param('ii', $this->mese, $this->fileH1[$z]->id_paziente);
$stmt_get_qta_prestaz_eff->execute();
$stmt_get_qta_prestaz_eff->store_result();
$stmt_get_qta_prestaz_eff->bind_result($qta_prestaz);
$stmt_get_qta_prestaz_eff->fetch();
$this->fileH2[$z]['qta_prestaz'] = str_pad($qta_prestaz, 3, "0", STR_PAD_LEFT);
$this->fileH2[$z]['codifica_nomencl'] = 't';
if ($this->fileH2[$z]['progressivo'] == '99')
$this->fileH2[$z]['codice_prestaz'] = ' ';
else $this->fileH2[$z]['codice_prestaz'] = '001.001';
$this->fileH2[$z]['esenzione_1'] = '0';
$this->fileH2[$z]['esenzione_2'] = ' ';
$this->fileH2[$z]['esenzione_3'] = '0';
$this->fileH2[$z]['onere'] = "1";
$this->fileH2[$z]['importo_compart'] = '000000,00';
$this->fileH2[$z]['posizione_compart'] = '0';
if ($this->fileH1[$z]->tariffa !== NULL || $this->fileH1[$z]->tariffa !== '')
$this->fileH2[$z]['importo_totale'] = str_replace('.', ',', str_pad(floatval($this->fileH1[$z]->tariffa) * $qta_prestaz, 9, "0", STR_PAD_LEFT));
else $this->fileH2[$z]['importo_totale'] = " ";
$stmt_get_qta_prestaz_eff->close();
$stmt_get_info_menomazioni->close();
$this->fileH2[$z]['posizione_contab'] = ' ';
$this->fileH2[$z]['err01'] = ' ';
$this->fileH2[$z]['err02'] = ' ';
$this->fileH2[$z]['err03'] = ' ';
$this->fileH2[$z]['err04'] = ' ';
$this->fileH2[$z]['err05'] = ' ';
$this->fileH2[$z]['err06'] = ' ';
$this->fileH2[$z]['err07'] = ' ';
$this->fileH2[$z]['err08'] = ' ';
$this->fileH2[$z]['err09'] = ' ';
$this->fileH2[$z]['err10'] = ' ';
$this->fileH2[$z]['anno_rif'] = $this->fileH1[$z]->anno_rif;
$this->fileH2[$z]['cod_strut_erog'] = decifra($_SESSION['cod_struttura_eroga']);
$this->fileH2[$z]['identificativo_mensile'] = $this->fileH1[$z]->identificativo_mensile;
$this->fileH2[$z]['anno_mese_invio'] = date('Ym');
$this->fileH2[$z]['asl_addebito'] = 000;
有人可以帮助我吗?
更新 1: 首先感谢大家的回答。 使用 JOIN 进行唯一调用,然后将准备语句移出循环。现在大约 500 个项目的时间是 5 分钟。
$sql = 'SELECT COUNT(tp.id),pc.data_autorizz, pc.data_inizio, pc.data_fine, tm.codice, tm.icd9_nuovo FROM tbl_pazienti_terapie_presenze as tp LEFT JOIN tbl_pazienti_contratti as pc ON tp.id_paziente = pc.id_paziente LEFT JOIN tbl_pazienti_terapie_menomazioni as tm ON tm.id_contratto = pc.id AND tm.id_paziente = pc.id_paziente WHERE MONTH(DATE(tp.ingresso_effettuato)) = ? AND tp.id_paziente = ? AND pc.id = ?';
$do_sql = $this->centro->prepare($sql);
$length = count($this->fileH1);
for ($z = 0; $z < $length; $z++)
$this->fileH2[$z]['id_paziente'] = $this->fileH1[$z]['id_paziente'];['id_terapia'];
$this->fileH2[$z]['regione'] = decifra($_SESSION['cod_regione']);
$this->fileH2[$z]['asl'] = decifra($_SESSION['cod_asl']);
$this->fileH2[$z]['cod_struttura'] = decifra($_SESSION['cod_struttura']);
$this->fileH2[$z]['tipo_assist'] = "RIA";
$this->fileH2[$z]['tipo_strutt'] = decifra($_SESSION['tipo_struttura']);
$do_sql->bind_param('iii', $this->mese, $this->fileH1[$z]['id_paziente'], $this->fileH1[$z]['id_contratto']);
$do_sql->execute();
$do_sql->store_result();
$do_sql->bind_result($qta_prestaz, $data_autorizz, $data_inizio, $data_fine, $cod_menomazione, $icd9_menomazione);
if ($do_sql->num_rows > 0)
$do_sql->fetch();
if ($cod_menomazione !== NULL && $cod_menomazione !== '')
$this->fileH2[$z]['cod_menomazione'] = $cod_menomazione;
else $this->fileH2[$z]['cod_menomazione'] = ' ';
if ($icd9_menomazione !== NULL && $icd9_menomazione !== '')
$this->fileH2[$z]['icd9_menomazione'] = str_pad($icd9_menomazione, 10, " ");
else $this->fileH2[$z]['icd9_menomazione'] = ' ';
if ($data_autorizz !== NULL && $data_autorizz !== '0000-00-00') $this->fileH2[$z]['data_prescrizione'] = date('dmY', strtotime($data_autorizz)); else $this->fileH2[$z]['data_prescrizione'] = ' ';
if ($data_inizio !== NULL && $data_inizio !== '0000-00-00') $this->fileH2[$z]['data_inizio_terapia'] = date('dmY', strtotime($data_inizio)); else $this->fileH2[$z]['data_inizio_terapia'] = ' ';
if ($data_fine !== NULL && $data_fine !== '0000-00-00') $this->fileH2[$z]['data_fine_terapia'] = date('dmY', strtotime($data_fine)); else $this->fileH2[$z]['data_fine_terapia'] = ' ';
if ($this->fileH1[$z]['tariffa'] !== NULL || $this->fileH1[$z]['tariffa'] !== '')
$this->fileH2[$z]['importo_totale'] = str_replace('.', ',', str_pad(floatval($this->fileH1[$z]->tariffa) * $qta_prestaz, 9, "0", STR_PAD_LEFT));
else $this->fileH2[$z]['importo_totale'] = ' ';
$this->fileH2[$z]['codifica_nomencl'] = 't';
if ($this->fileH2[$z]['progressivo'] == '99')
$this->fileH2[$z]['codice_prestaz'] = ' ';
else $this->fileH2[$z]['codice_prestaz'] = '001.001';
$this->fileH2[$z]['esenzione_1'] = '0';
$this->fileH2[$z]['esenzione_2'] = ' ';
$this->fileH2[$z]['esenzione_3'] = '0';
$this->fileH2[$z]['onere'] = "1";
$this->fileH2[$z]['importo_compart'] = '000000,00';
$this->fileH2[$z]['posizione_compart'] = '0';
// CALCOLO LE DATE DEL CICLO DI FATTURAZIONE
$data_inizio_mese_attuale = date('Y-m-1');
$data_inizio_terapia = date('Y-m-d', strtotime($data_inizio));
if ($data_inizio_mese_attuale < $data_inizio_terapia)
$this->fileH2[$z]['data_inizio_periodo_fatturazione'] = date('dmY', strtotime($data_inizio_terapia));
else $this->fileH2[$z]['data_inizio_periodo_fatturazione'] = date('dmY', strtotime($data_inizio_mese_attuale));
$data_fine_mese_attuale = date('Y-m-t'); // t = ultimo gg del mese attuale
$data_fine_terapia = date('Y-m-d', strtotime($data_fine));
if ($data_fine_mese_attuale > $data_fine_terapia)
$this->fileH2[$z]['data_fine_periodo_fatturazione'] = date('dmY', strtotime($data_fine_terapia));
else $this->fileH2[$z]['data_fine_periodo_fatturazione'] = date('dmY', strtotime($data_fine_mese_attuale));
$this->fileH2[$z]['num_registro'] = $this->fileH1[$z]['anno_rif'] . decifra($_SESSION['cod_asl']) . '0' . date('y') . str_pad($_POST['mese'], 2, '0', STR_PAD_LEFT) . '0001';; // "203021040001"; // AGGIUNGERE numero registro struttura
$this->fileH2[$z]['medico_autorizz'] = ' ';
$this->fileH2[$z]['cod_medico_autorizz'] = ' ';
$this->fileH2[$z]['istat_primo_ricovero'] = '000000';
$this->fileH2[$z]['progressivo'] = $this->fileH1[$z]['progressivo'];
$this->fileH2[$z]['posizione_contab'] = ' ';
$this->fileH2[$z]['err01'] = ' ';
$this->fileH2[$z]['err02'] = ' ';
$this->fileH2[$z]['err03'] = ' ';
$this->fileH2[$z]['err04'] = ' ';
$this->fileH2[$z]['err05'] = ' ';
$this->fileH2[$z]['err06'] = ' ';
$this->fileH2[$z]['err07'] = ' ';
$this->fileH2[$z]['err08'] = ' ';
$this->fileH2[$z]['err09'] = ' ';
$this->fileH2[$z]['err10'] = ' ';
$this->fileH2[$z]['anno_rif'] = $this->fileH1[$z]['anno_rif'];
$this->fileH2[$z]['cod_strut_erog'] = decifra($_SESSION['cod_struttura_eroga']);
$this->fileH2[$z]['identificativo_mensile'] = $this->fileH1[$z]['identificativo_mensile'];
$this->fileH2[$z]['anno_mese_invio'] = date('Ym');
$this->fileH2[$z]['asl_addebito'] = 000;
$do_sql->close();
比以前好,但仍然不能接受
更新 2:
经过一些测试,我发现这个查询COUNT(id) FROM tbl_pazienti_terapie_presenze WHERE MONTH(ingresso_effettuato) = ? AND id_paziente = ?
导致严重减速。我不知道为什么会这样。
解决方案:
谢谢大家的回答。
问题是查询函数 MONTH()。尽管该字段已编入索引,但函数 MONTH() 会跳过索引并因此减慢查询速度。
将其替换为
例如WHERE ingresso_effettuato BETWEEN '2021-12-01 00:00:00', '2021-12-31 23:59:59'
或WHERE ingresso_effettuato >= '2021-12-01 00:00:00' AND ingresso_effettuato <= '2021-12-31 23:59:59'
问题已解决。
【问题讨论】:
小点 将 IF 语句塞进一行并不能加快速度,只会减慢跟随你的可怜的开发人员的速度,并且必须弄清楚发生了什么 请告诉我们SHOW KEYS FROM tbl_pazienti_terapie_menomazioni
的结果。请为您使用的每张桌子。
您尝试过检查哪些部分速度较慢?您尝试过什么来解决问题本身?
if (x !== NULL || x != '')
的一般逻辑总是正确的。可能意味着使用&&
。
如果您遇到任何与数据库相关的问题,请分享更多详细信息,例如慢查询的执行计划
【参考方案1】:
您的代码运行缓慢的原因有很多。
在 php 中,这通常是由滥用数据库引起的。首先,您需要在表上设置索引,尤其是对于您用于 SELECT 语句的字段。
此外,由于 PHP 必须通过网络连接到数据库,如果您的数据库位于与您的网络服务器不同的服务器上,您的代码可能会更慢。一些托管服务提供商对数据库使用不同的网络。因此,尽可能少地调用数据库至关重要。
在您的脚本中,我看到您使用了 prepare()、execute()、close() 和这 3 次。这意味着您进入数据库并在那里执行一些操作。这也可能导致性能下降。
我做了一个小小的性能测试,表明在循环中使用 preapre 语句和在 ouside 中使用 preapre 语句是有区别的:
<?php
error_reporting(E_ALL);
ini_set('display_errors','On');
$mysqli = new mysqli('db','db','db','db');
$queries = 100;
$start = microtime(true);
$resultId = null;
$resultTest = null;
for($i=0;$i<=100;$i++)
$id = 1;
$sql = "SELECT id,test FROM test WHERE id = ?";
$statement = $mysqli->prepare($sql);
$statement->bind_param('i',$id);
$statement->execute();
$statement->store_result();
$statement->bind_result($resultId,$resultTest);
$statement->close();
$id = 2;
$sql = "SELECT id,test FROM test WHERE id = ?";
$statement = $mysqli->prepare($sql);
$statement->bind_param('i',$id);
$statement->execute();
$statement->store_result();
$statement->bind_result($resultId,$resultTest);
$statement->close();
$id = 3;
$sql = "SELECT id,test FROM test WHERE id = ?";
$statement = $mysqli->prepare($sql);
$statement->bind_param('i',$id);
$statement->execute();
$statement->store_result();
$statement->bind_result($resultId,$resultTest);
$statement->close();
$end = microtime(true);
$diff = $end-$start;
echo "Prepare statements inside loop time: ".$diff."<br/>";
$start = microtime(true);
$sql = "SELECT id,test FROM test WHERE id = ?";
$statement1 = $mysqli->prepare($sql);
$statement2 = $mysqli->prepare($sql);
$statement3 = $mysqli->prepare($sql);
for($i=0;$i<=100;$i++)
$id = 1;
$statement1->bind_param('i',$id);
$statement1->execute();
$statement1->store_result();
$statement1->bind_result($resultId,$resultTest);
$id = 2;
$statement2->bind_param('i',$id);
$statement2->execute();
$statement2->store_result();
$statement2->bind_result($resultId,$resultTest);
$id = 3;
$statement3->bind_param('i',$id);
$statement3->execute();
$statement3->store_result();
$statement3->bind_result($resultId,$resultTest);
$statement1->close();
$statement2->close();
$statement3->close();
$end = microtime(true);
$diff = $end-$start;
echo "Prepare statements only execute time: ".$diff."<br/>";
结果是
Prepare statements inside loop time: 0.046118021011353
Prepare statements only execute time: 0.020095109939575
所以我建议首先将您的语句移出循环,然后检查您的索引。
这样做。您可以用 Real 值写下您的 SQL 查询之一,并在 PHPMyadmin 中使用 DESCRIBE 执行您的 SELECT 语句
例如
DESCRIBE SELECT COUNT(id) FROM tbl_pazienti_terapie_presenze WHERE MONTH(DATE(ingresso_effettuato)) = 12 AND id_paziente = 1336
在结果中你会看到是否有索引被使用,如果没有,那么你需要为这个字段创建索引。
而且 SQL 查询通常可以与连接组合,因为您在 3 个不同的 SQL 查询中重复使用 ID,这些表必须以某种方式关联,以便它们可以连接到一个语句中。
我不确定这个答案是否能解决您的速度问题,但至少您有一些可以优化的线索。
【讨论】:
您不必这样做,但这是一种更好的方法 @RiggsFolly 但他在循环中有 3 个准备好的语句,并且他在循环中关闭了连接,我很确定他的每次连接都可能出现性能问题 是的,你是对的,但是这个词必须假设如果你不这样做就会有错误并且不会有错误,只是一个缓慢的循环:) 请分享更多细节,以便其他人可以从您的回答中学习。这真的是慢循环的修复吗?还是只是应该作为评论发布的附带评论? 另外,在不知道$this->centro->prepare
返回什么的情况下,猜测调用close
将真正关闭整个数据库连接。例如,PHP 自己的 PDO 类没有内置函数称为close
以上是关于非常慢的循环PHP的主要内容,如果未能解决你的问题,请参考以下文章