提问人:Giuseppe Russo 提问时间:12/21/2021 最后编辑:Giuseppe Russo 更新时间:12/23/2021 访问量:697
非常慢的循环 PHP
Very slow loop PHP
问:
我是一名初级开发人员,我对脚本的循环有问题。 循环循环大型数组来自数据库。 问题是在最短的时间内完成循环是可能的,但就目前而言,大约 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 进行唯一调用,并将 prepare 语句移出循环。现在大约 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'
答:
代码可能很慢的原因有很多。
在PHP中,它通常是由滥用数据库引起的。首先,您需要在表上设置索引,尤其是对于用于 SELECT 语句的字段。
此外,由于PHP必须通过网络连接到数据库,因此如果您的数据库与Web服务器位于不同的服务器上,则代码可能会变慢。一些托管服务提供商对数据库使用不同的网络。因此,尽可能少地调用数据库至关重要。
在您的脚本中,我看到您使用了 prepare()、execute()、close() 和 this 3 次。这意味着您转到数据库并在那里执行一些操作。这也可能导致性能降低。
我做了一个小小的性能测试,以表明在循环中使用 preapre 语句和 ouside 是有区别的:
<?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
因此,我建议首先将语句移出循环,然后检查索引。
这样做。您可以在 PHPMyadmin 中写下带有 Real 值的 SQL 查询之一,并使用 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,因此表必须以某种方式具有相关性,以便它们能够联接到一个语句中。
我不确定这个答案是否会解决您的速度问题,但至少您有一些可以优化的线索。
评论
$this->centro->prepare
close
close
评论
SHOW KEYS FROM tbl_pazienti_terapie_menomazioni
if (x !== NULL || x != '')
&&