非常慢的循环 PHP

Very slow loop PHP

提问人:Giuseppe Russo 提问时间:12/21/2021 最后编辑:Giuseppe Russo 更新时间:12/23/2021 访问量:697

问:

我是一名初级开发人员,我对脚本的循环有问题。 循环循环大型数组来自数据库。 问题是在最短的时间内完成循环是可能的,但就目前而言,大约 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 循环 for-loop mysqli

评论

1赞 RiggsFolly 12/21/2021
小点将 IF 语句塞进一行并不能加快任何速度,它只会减慢跟随您的可怜开发人员的速度,并且必须弄清楚发生了什么
2赞 HoldOffHunger 12/21/2021
请向我们展示 .对于您正在使用的每张桌子,请。SHOW KEYS FROM tbl_pazienti_terapie_menomazioni
0赞 Nico Haase 12/21/2021
您尝试检查哪些部分很慢?你试图用什么来解决问题本身?
0赞 12/21/2021
的一般逻辑总是正确的。可能意味着使用 .if (x !== NULL || x != '')&&
1赞 Nico Haase 12/21/2021
如果您遇到任何与数据库相关的问题,请分享更多详细信息,例如慢查询的执行计划

答:

0赞 Vitalij Mik 12/21/2021 #1

代码可能很慢的原因有很多。

在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,因此表必须以某种方式具有相关性,以便它们能够联接到一个语句中。

我不确定这个答案是否会解决您的速度问题,但至少您有一些可以优化的线索。

评论

0赞 RiggsFolly 12/21/2021
你不必这样做,但这是一种更好的方法
0赞 Vitalij Mik 12/21/2021
@RiggsFolly但他在循环中有 3 个准备好的语句,并且他还关闭了循环中的连接,我非常确定他的 MIGHT 有性能问题需要在每次连接时打开
1赞 RiggsFolly 12/21/2021
是的,你是对的,但是如果你不这样做,这个词必须假设会有错误,也不会有错误,只是一个缓慢的循环:)
0赞 Nico Haase 12/21/2021
请分享更多细节,以便其他人可以从您的答案中学习。这真的是慢速循环的修复方法吗?或者只是一个应该作为评论发布的旁白?
0赞 Nico Haase 12/21/2021
此外,在不知道返回什么的情况下,它猜测调用它真的会关闭整个数据库连接。例如,PHP 自己的 PDO 类没有一个名为$this->centro->preparecloseclose