PHP 中的 Oracle SQL 请求不返回任何内容,但在 SQL Developer 中有效

Posted

技术标签:

【中文标题】PHP 中的 Oracle SQL 请求不返回任何内容,但在 SQL Developer 中有效【英文标题】:Oracle SQL request in PHP not returning anything but works in SQL Developer 【发布时间】:2021-06-28 05:11:30 【问题描述】:

我遇到了一个问题,我有一个请求可以在 SQL DeveloperVsCode 中使用 oracle DB 附加组件,但在 php 代码中,它返回 0 行。 PHP 版本太旧了,我对此无能为力,5.4.6OracleDB 是 11g (11.2.0.3.0)。

这是请求:

$sql =  "SELECT
                PATIENT_SOIGNE.NOM,
                PATIENT_SOIGNE.PATRONYME AS NOM_NAISSANCE,
                PATIENT_SOIGNE.PRENOM,
                to_date(PATIENT_SOIGNE.DATENAIS,'YYYYMMDD') AS DDN,
                decode(PATIENT_SOIGNE.SEXE,'F','Femme','M','Homme','Inconnu') AS SEXE,
                PATIENT_SOIGNE.NIP,
                S_ACTE_MED.CODEP1 || ' - ' || ( S_ACTE_MED.LIBELLE ) AS ACTE,
                OPERATEUR.NOM AS NOM_OPERATEUR,
                OPERATEUR.PRENOM AS PRENOM_OPERATEUR
            FROM
                PENSOINS.ACTE_RDV,
                PENSOINS.RDV_STATUT  RDV_STATUT,
                PENSOINS.ACTE_RDV_GRP,
                PENSOINS.S_ACTE_M  S_ACTE_MED,
                PENSOINS.EJ_PERSO  OPERATEUR,
                (
                    SELECT RESSOURCE_OPERATEUR.*,  ACTE_RDV_RESA.NIACTERDV
                    FROM
                        PENSOINS.DXP_RESSOURCE RESSOURCE_OPERATEUR, PENSOINS.ACTE_RDV_RESA ACTE_RDV_RESA
                    WHERE
                        RESSOURCE_OPERATEUR.ID_RESSOURCE=ACTE_RDV_RESA.REF_RESSOURCE
                        AND
                            RESSOURCE_OPERATEUR.REF_TYPE_RESSOURCE=1
                )  TD_RESSOURCE_OPERATEUR,
                PENSOINS.EJ_SRV  RDV_SRV,
                PENSOINS.VENUE,
                (
                    SELECT A.NIACTERDV, A.NIVENUE, TD.NISEJMOUV, V.NIPATIENT, nvl(TD.NISEJOUR, V.NISEJOUR) as NISEJOUR, V.NISERVICE
                    FROM PENSOINS.ACTE_RDV  A,
                        PENSOINS.VENUE      V,
                        (
                            SELECT L.NIACTERDV, L.NIVENUE, DER.DATE_DER_MAJ, L.NISEJMOUV, M.NISEJOUR
                            FROM PENSOINS.LIEN_RDV_MVT L,
                                PENSOINS.MOUVEMEN     M,
                            (
                                SELECT A.NIACTERDV, A.NIVENUE, max(nvl(L.DATE_MODIF, L.DATE_CREA)) DATE_DER_MAJ
                                FROM PENSOINS.ACTE_RDV     A,
                                    PENSOINS.LIEN_RDV_MVT L
                                WHERE A.NIACTERDV = L.NIACTERDV
                                AND L.RETRAIT = 'F'
                                GROUP BY A.NIVENUE,A.NIACTERDV
                            ) DER
                            WHERE L.NISEJMOUV = M.NISEJMOUV
                            AND DER.NIACTERDV = L.NIACTERDV
                            AND DER.NIVENUE = L.NIVENUE
                            AND DER.DATE_DER_MAJ = nvl(L.DATE_MODIF, L.DATE_CREA)
                            AND L.RETRAIT = 'F'
                        ) TD
                    WHERE A.NIVENUE(+) = V.NIVENUE
                    AND TD.NIACTERDV(+) = A.NIACTERDV
                )  TD_VENUE,
                PENSOINS.PATIENT  PATIENT_SOIGNE
            WHERE
                ( PENSOINS.ACTE_RDV.STATUTRDV=RDV_STATUT.NI(+)  )
                AND  ( PENSOINS.ACTE_RDV_GRP.NIACTERDV=PENSOINS.ACTE_RDV.NIACTERDV  )
                AND  ( S_ACTE_MED.NIACTE=PENSOINS.ACTE_RDV_GRP.NIACTE and S_ACTE_MED.NISERVICE=PENSOINS.ACTE_RDV_GRP.NISERVICE  )
                AND  ( OPERATEUR.NIUTILISAT(+)=TD_RESSOURCE_OPERATEUR.ID_DXCARE  )
                AND  ( TD_RESSOURCE_OPERATEUR.NIACTERDV(+)=PENSOINS.ACTE_RDV.NIACTERDV  )
                AND  ( PENSOINS.ACTE_RDV.NISERVICE=RDV_SRV.NISERVICE(+)  )
                AND  ( PENSOINS.VENUE.NIVENUE=TD_VENUE.NIVENUE  )
                AND  ( TD_VENUE.NIACTERDV=PENSOINS.ACTE_RDV.NIACTERDV(+)  )
                AND  ( PATIENT_SOIGNE.NIPATIENT(+)=PENSOINS.VENUE.NIPATIENT  )
                AND  RDV_STATUT.CODE|| ' - ' ||RDV_STATUT.LIBELLE  IN  ( 'R - Attribué','M - Modifié'  )
                AND  RDV_SRV.CODE||' - '||RDV_SRV.NOM  IN  ( '07 - Diététique','08 - Endocrinologie','18 - Oncologie','19 - Ophtalmologie','24 - Pneumologie','30 - Rhumatologie','34 - Orthophonie','40 - Consultations externes','42 - Stomatologie','43 - HOPITAL DE JOUR MEDECINE','44 - Hématologie','20 - ORL','CE 02 - Anesthésie','CE 03 - Cardiologie','CE 04 - Chirurgie Orthopédique','CE 06 - Chirurgie Urologique','CE 07 - Chirurgie Viscérale','CE 08 - Hépato-gastro-entérologie','CE 09 - Chirurgie Plastique','CE 10 - Gériatrie','CE 11 - Gynécologie-Obstétrique','CE 12 - Médecine interne','CE 14 - Pédiatrie','CE 15 - Tabacologie','CE 16 - Chirurgie Vasculaire','16 - Néphrologie'  )
                AND  substr(PENSOINS.ACTE_RDV.HORAIRE, 1, 8)  =  '".$date_next."'
            GROUP BY
                PATIENT_SOIGNE.NOM,
                PATIENT_SOIGNE.PATRONYME,
                PATIENT_SOIGNE.PRENOM,
                to_date(PATIENT_SOIGNE.DATENAIS,'YYYYMMDD'),
                decode(PATIENT_SOIGNE.SEXE,'F','Femme','M','Homme','Inconnu'),
                PATIENT_SOIGNE.NIP,
                S_ACTE_MED.CODEP1 || ' - ' || ( S_ACTE_MED.LIBELLE ),
                OPERATEUR.NOM,
                OPERATEUR.PRENOM";

以及使用它的代码:

$date_next = date_next_day();

$db_o = connexion_oracle_dxcare();
$parsed = oci_parse($db_o, $sql);
$ru=oci_execute($parsed);

if (!$ru)  //doesn't show anything
    $e = oci_error($parsed);
    print htmlentities($e['message']);
    print "\n<pre>\n";
    print htmlentities($e['sqltext']);
    printf("\n%".($e['offset']+1)."s", "^");
    print  "\n</pre>\n";
    

$num_rows = oci_fetch_all($parsed, $res); //returns 0 rows
var_dump($res); //show me all the column names but no datas inside

日期格式很好,我的函数返回这种格式 'YYYYMMDD' 有效。 var 转储显示:

array(9)  ["NOM"]=> array(0)   ["NOM_NAISSANCE"]=> array(0)   ["PRENOM"]=> array(0)   ["DDN"]=> array(0)   ["SEXE"]=> array(0)   ["NIP"]=> array(0)   ["ACTE"]=> array(0)   ["NOM_OPERATEUR"]=> array(0)   ["PRENOM_OPERATEUR"]=> array(0)   

我肯定错过了一些东西,但我找不到它。有什么想法吗?

编辑:

所以我测试了问题是否出在口音上,但是这个请求:

SELECT PENSOINS.EJ_SRV.NOM from PENSOINS.EJ_SRV WHERE PENSOINS.EJ_SRV.NOM LIKE 'Dié%'

工作正常(即使它随后在浏览器中显示为 é)。

我改变了这部分:

AND  substr(PENSOINS.ACTE_RDV.HORAIRE, 1, 8)  =  '".$date_next."'  
            GROUP BY  
                PATIENT_SOIGNE.NOM,  
                PATIENT_SOIGNE.PATRONYME,  
                PATIENT_SOIGNE.PRENOM,  
                to_date(PATIENT_SOIGNE.DATENAIS,'YYYYMMDD'),  

多次,使用了一些 to_char、trunc、to_date 但没有任何效果。

关于日期,我能够发表以下声明:

SELECT PENSOINS.ACTE_RDV.HORAIRE from PENSOINS.ACTE_RDV WHERE substr(PENSOINS.ACTE_RDV.HORAIRE, 1, 8) = :date_test_bv  

工作。这里:date_test_bv 是一个字符串的绑定变量,比如20140415 所以现在我回来了

AND  RDV_SRV.CODE||' - '||RDV_SRV.NOM  IN  ( '07 - Diététique','08 - Endocrinologie','18 - Oncologie','19 - Ophtalmologie','24 - Pneumologie','30 - Rhumatologie','34 - Orthophonie','40 - Consultations externes','42 - Stomatologie','43 - HOPITAL DE JOUR MEDECINE','44 - Hématologie','20 - ORL','CE 02 - Anesthésie','CE 03 - Cardiologie','CE 04 - Chirurgie Orthopédique','CE 06 - Chirurgie Urologique','CE 07 - Chirurgie Viscérale','CE 08 - Hépato-gastro-entérologie','CE 09 - Chirurgie Plastique','CE 10 - Gériatrie','CE 11 - Gynécologie-Obstétrique','CE 12 - Médecine interne','CE 14 - Pédiatrie','CE 15 - Tabacologie','CE 16 - Chirurgie Vasculaire','16 - Néphrologie'  )  

AND  substr(PENSOINS.ACTE_RDV.HORAIRE, 1, 8)  =  :date_bv  

GROUP BY  

PATIENT_SOIGNE.NOM,
PATIENT_SOIGNE.PATRONYME,  
PATIENT_SOIGNE.PRENOM,  
to_date(PATIENT_SOIGNE.DATENAIS,'YYYYMMDD'),  
decode(PATIENT_SOIGNE.SEXE,'F','Femme','M','Homme','Inconnu'),  
PATIENT_SOIGNE.NIP,  
S_ACTE_MED.CODEP1 || ' - ' || ( S_ACTE_MED.LIBELLE ),  
OPERATEUR.NOM,  
OPERATEUR.PRENOM  

知道绑定的字符串日期会产生成功的结果,并且使用重音符号不是问题。 我有 0 条错误消息,也不是来自 PHP 或 Oracle,只是像我之前显示的 var 转储一样的空行。 那时我真的不明白问题出在哪里。

【问题讨论】:

使用像AND substr(PENSOINS.ACTE_RDV.HORAIRE, 1, 8) = '".$date_next."'这样的连接是一个性能问题和一个SQL注入安全问题。您必须使用绑定变量。 首先添加一些错误检查。添加error_reporting(E_ALL); ini_set('display_errors', 'On'); 并检查来自OCI 调用的适当返回值,请参阅文档php.net/manual/en/function.oci-error.php 【参考方案1】:

隐式日期转换负责大多数在不同客户端中以不同方式工作的查询。日期格式默认为在 system 级别设置的 NLS_DATE_FORMAT 值,但许多客户端会通过在 session 级别设置参数来覆盖该值。

具体来说,这两个表达式可能有问题:

substr(PENSOINS.ACTE_RDV.HORAIRE, 1, 8)  =  '".$date_next."
...
to_date(PATIENT_SOIGNE.DATENAIS,'YYYYMMDD')

许多人通过强制所有客户端更改其设置来解决这些问题,但更好的永久解决方案始终是指定所需的格式,或者完全避免日期转换。假设这两列是日期值,将表达式重写为这样会更安全:

substr(to_char(PENSOINS.ACTE_RDV.HORAIRE, 1, 8), 'YYYYMMDD')  =  '".$date_next."'
...
trunc(PATIENT_SOIGNE.DATENAIS)

(我不确定 PHP 是如何工作的,可能有更好的方法将 $date_next 作为日期而不是字符串传递。)

另一个疯狂的猜测是非 ASCII 字符串在某处引起了问题:

RDV_SRV.CODE||' - '||RDV_SRV.NOM  IN  ( '07 - Diététique', ...

查看select sql_fulltext from gv$sql where lower(sql_fulltext) like '%07 - Di%; 的输出以确保查询正确传递给Oracle。如果某些文件或进程不支持非 ASCII 字符,那么您可能需要像这样存储 Unicode 字符串:

RDV_SRV.CODE||' - '||RDV_SRV.NOM  IN  ( '07 - Di'||unistr('\00E9')|'t'||unistr('\00E9')||'tique', ...

【讨论】:

如果字符集有问题,连接时设置字符集。见php.net/manual/en/function.oci-connect.php$conn = oci_connect('hr', 'welcome', 'localhost/XE', 'AL32UTF8');中的示例3 您好,我在进行了更多测试后编辑了我的问题。似乎编码或日期都不是这里的问题。 (除非我当然错过了什么!)

以上是关于PHP 中的 Oracle SQL 请求不返回任何内容,但在 SQL Developer 中有效的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL oracle 过程不返回任何值

限制从 Oracle 中的 SQL 查询返回的记录

Oracle SQL 中的最小值但不为 NULL

c# Oracle SQL 语句通配符

从 pl/sql 中的函数返回数据作为光标而不创建类型 oracle 11g

php怎么判断字段是不是为空