PHP version of QueryofQuery


PHP version of QueryofQuery

TL;DR:我如何修改下面的queryOfQuery()函数以与OCI8/Oracle后端一起工作?

在寻找与CFML的查询的查询相同的PHP时,我遇到了这个问题,它指向Tom Muck写的一个函数:

function queryOfQuery($rs, // The recordset to query
  $fields = "*", // optional comma-separated list of fields to return, or * for all fields 
  $distinct = false, // optional true for distinct records
  $fieldToMatch = null, // optional database field name to match
  $valueToMatch = null) { // optional value to match in the field, as a comma-separated list
  $newRs = Array();
  $row = Array();
  $valueToMatch = explode(",",$valueToMatch);
  $matched = true;
  mysql_data_seek($rs, 0);
  if($rs) {
    while ($row_rs = mysql_fetch_assoc($rs)){
      if($fields == "*") {
        if($fieldToMatch != null) {
          $matched = false;
          if(is_integer(array_search($row_rs[$fieldToMatch],$valueToMatch))){ 
            $matched = true;
          }
        }
        if($matched) $row = $row_rs;
      }else{
        $fieldsArray=explode(",",$fields);
        foreach($fields as $field) {
          if($fieldToMatch != null) {
            $matched = false;
            if(is_integer(array_search($row_rs[$fieldToMatch],$valueToMatch))){ 
              $matched = true;
            }
          }
          if($matched) $row[$field] = $row_rs[$field];
        }
      } 
      if($matched)array_push($newRs, $row);
    };
    if($distinct) {
      sort($newRs);
      for($i = count($newRs)-1; $i > 0; $i--) {
        if($newRs[$i] == $newRs[$i-1]) unset($newRs[$i]);
      }
    }
  }
  mysql_data_seek($rs, 0);
  return $newRs;
}

我想调整这段代码以适应我们的数据库环境(我们使用Oracle和OCI8—而不是MySQL,该函数是为其编写的)。不幸的是,我刚刚掌握的PHP技能使我无法胜任这项任务。具体来说,我可以看到mysql_data_seek()mysql_fetch_assoc()很可能被OCI8替代。代入的等价物是什么?还有什么需要调整的吗?

修改函数以使用OCI是微不足道的,但不幸的是,您失去了从结果集中重复获取的能力。

我已经在下面发布了完整的函数,但只有三行需要改变。我已经注释掉了现有的行,以显示更改的位置。

mysql_data_seek()的调用被简单地移除。如果已经执行了任何提取,第一个调用将结果集指针重置为第一个记录。这确保"子查询"获得所有适用的记录。第二次调用再次重置指针,以确保函数执行后的进一步处理将从结果集的开头开始。

我查看了OCI8函数列表,但看不到重置指针的任何等效函数。我认为你可以使用游标,但我不懂PL/SQL,所以不能给你任何帮助。

总之,你可以"查询一个查询",但有两个注意事项:

  1. 在调用函数之前获取的任何行将不包括在内。
  2. 不能在函数运行后从结果集中获取。

function queryOfQuery($rs, // The recordset to query
  $fields = "*", // optional comma-separated list of fields to return, or * for all fields 
  $distinct = false, // optional true for distinct records
  $fieldToMatch = null, // optional database field name to match
  $valueToMatch = null) { // optional value to match in the field, as a comma-separated list
  $newRs = Array();
  $row = Array();
  $valueToMatch = explode(",",$valueToMatch);
  $matched = true;
  //mysql_data_seek($rs, 0);
  if($rs) {
    //while ($row_rs = mysql_fetch_assoc($rs)){
    while ($row_rs = oci_fetch_assoc($rs)){
      if($fields == "*") {
        if($fieldToMatch != null) {
          $matched = false;
          if(is_integer(array_search($row_rs[$fieldToMatch],$valueToMatch))){ 
            $matched = true;
          }
        }
        if($matched) $row = $row_rs;
      }else{
        $fieldsArray=explode(",",$fields);
        foreach($fields as $field) {
          if($fieldToMatch != null) {
            $matched = false;
            if(is_integer(array_search($row_rs[$fieldToMatch],$valueToMatch))){ 
              $matched = true;
            }
          }
          if($matched) $row[$field] = $row_rs[$field];
        }
      } 
      if($matched)array_push($newRs, $row);
    };
    if($distinct) {
      sort($newRs);
      for($i = count($newRs)-1; $i > 0; $i--) {
        if($newRs[$i] == $newRs[$i-1]) unset($newRs[$i]);
      }
    }
  }
  //mysql_data_seek($rs, 0);
  return $newRs;
}

编辑:我对此进行了更深入的研究,我认为这对CFML开发人员是一种误导,因为PHP和CFML在术语和方法上存在差异。汤姆·穆克似乎误解了这些区别,因此无意中歪曲了他的函数是如何工作的。

阅读完CFML帮助文档后,我相信CFML 中的查询隐式将所有行获取到web服务器内存中。这就是允许结果集被再次"查询"而不与数据库服务器通信的原因。

在PHP中,开发人员选择的函数控制这个行为(所以s/他做了一个显式的选择)。标准行为将是(为了简洁而简化):

  1. 打开数据库连接
  2. 执行查询
  3. 检索结果集

步骤3通常是迭代执行的;每次从数据库中获取和处理一行(与Tom的函数完全相同)。但也有可能将整个结果集获取到web服务器内存中,然后处理它。对于OCI,这将是oci_fetch_all(),它返回一个2D数组。这正是Tom误解PHP行为的地方。

这意味着原始函数不提供CFML开发人员所描述或期望的相同功能。要做到这一点,您需要将所有数据获取到一个数组中,然后迭代它。最后,这取决于开发者的偏好和潜在的性能优化。你:

  • 获取和处理每一行,保持内存使用低,但可能看到缓慢的性能,由于不断从数据库服务器接收。
  • 获取所有行,可能使用大量内存,但也看到直接从web服务器内存工作可能的性能改进。

最后,除非你在运行一个非常大规模的web应用程序,否则我会说性能差异可以忽略不计。


编辑2:

这个调整后的queryOfQuery函数不绑定到任何特定的数据库;它循环一个给定的数组(rs),不管它是如何生成的:

function queryOfQuery(
    $rs, // The recordset to query
    $fields = "*", // optional comma-separated list of fields to return, or * for all fields 
    $distinct = false, // optional true for distinct records
    $fieldToMatch = null, // optional database field name to match
    $valueToMatch = null // optional unless $fieldToMatch is specified; value to match in the field, as a comma-separated list
) {
    $newRs = Array();
    $row = Array();
    $valueToMatch = explode(",",$valueToMatch);
    $matched = true;
    if($rs) {
        // $issue$ this loop over the results should be moved to lines 20 and 31
        foreach($rs AS $row_rs){
            if($fields == "*") {
                if($fieldToMatch != null) {
                    $matched = false;
                    if(is_integer(array_search($row_rs[$fieldToMatch],$valueToMatch))){ 
                        $matched = true;
                    };
                };
                if($matched) $row = $row_rs;
            }
            else {
                $fieldsArray=explode(",",$fields);
                foreach($fieldsArray as $field) {
                    if($fieldToMatch != null) {
                        $matched = false;
                        if(is_integer(array_search($row_rs[$fieldToMatch],$valueToMatch))){ 
                            $matched = true;
                        };
                    };
                    if($matched) $row[$field] = $row_rs[$field];
                };
            };
            if($matched)array_push($newRs, $row);
        };
        if($distinct) {
            sort($newRs);
            for($i = count($newRs)-1; $i > 0; $i--) {
                if($newRs[$i] == $newRs[$i-1]) unset($newRs[$i]);
            };
        };
    };
    return $newRs;
};