【问题标题】:Compare one query with multiple results in PHP在 PHP 中将一个查询与多个结果进行比较
【发布时间】:2015-08-06 09:10:36
【问题描述】:

我有两个文本输入。像这样:

所以,我有某种动态 ajax 搜索。我传递输入数据并进行两个不同的 mysql 选择。像这样的:

表格最终图 - SELECT 1

id -------- latitud-----longitud---

1        |  6.2523915 | -75.5737028 |
2        |  6.2640349 | -75.5990783 |
3        |  6.2642411 | -75.5999791 |
4        |  6.2638461 | -75.5982590 |
-------------------------------------

表格最终图 - SELECT 2

id -------- latitud-----longitud---

6        |  6.262669 | -75.596799 |
7        |  6.258019 | -75.598001 |
8        |  6.253668 | -75.599374 |
9        |  6.250724 | -75.602335 |
-------------------------------------

所以,我想将每个“纬度和经度字段”与 SELECT2 的所有“纬度”和“经度”字段进行比较:

我有这个 PHP,我必须做一些改进,但可以说它有效:

<?php
$buscar = $_POST['b'];
$buscarcarrera = $_POST['c'];
$whatIWant = substr($buscar, strpos($buscar, "Calle") + 5);
$whatIWant2 = substr($buscarcarrera, strpos($buscarcarrera, "Carrera") + 5);
$vacio = "Calle50A";
$vacioc = "Carrera50A";

if (preg_match('/[A-Za-z]/', $whatIWant))
    {
    buscar($buscar, "", $buscarcarrera, "");
    }
  else
    {
    buscar($buscar, $vacio, $buscarcarrera, $vacioc);
    }

function buscar($b, $exclusion, $buscarcarrera, $exclusion2)
    {
    $con = mysql_connect('localhost', 'root', '');
    mysql_select_db('map', $con);
    $sql = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $b . "%' AND calle not in ('$exclusion')", $con);
    $contar = mysql_num_rows($sql);
    if ($contar == 0)
        {
        echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
        }
      else
        {
        while ($row = mysql_fetch_array($sql))
            {
            $nombre = $row['calle'];
            $id = $row['id'];
            $lat = $row['latitud'];
            $lon = $row['longitud'];
            }
        }

    $sql2 = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $buscarcarrera . "%' AND calle not in ('$exclusion2')", $con);
    $contar2 = mysql_num_rows($sql2);
    if ($contar2 == 0)
        {
        echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
        }
      else
        {
        while ($row2 = mysql_fetch_array($sql2))
            {
            $nombre2 = $row2['calle'];
            $id2 = $row2['id'];
            $lat2 = $row2['latitud'];
            $lon2 = $row2['longitud'];
            }
        }
    }

function distance($lat1, $lon1, $lat2, $lon2, $unit)
    {
    $theta = $lon1 - $lon2;
    $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
    $dist = acos($dist);
    $dist = rad2deg($dist);
    $miles = $dist * 60 * 1.1515;
    $unit = strtoupper($unit);
    if ($unit == "K")
        {
        return ($miles * 1.609344);
        }
      else
    if ($unit == "N")
        {
        return ($miles * 0.8684);
        }
      else
        {
        return $miles;
        }
    }

echo distance(32.9697, -96.80322, 29.46786, -98.53506, "M") . " Miles<br />";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "K") . " Kilometers<br />";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "N") . " Nautical Miles<br />";
?>

那么:如何使用函数比较每个值来确定坐标之间的接近度(使用我的distance()函数)? 我的问题是我不知道如何比较第一个查询的每个点与第二个查询的每个点之间的距离,以及所有可能的组合。

我想要类似的函数 compare (lat1,lon1,lat2,lon2); (lat1,lon1,lat3,lon3),(lat1,lon1,lat4,lon4),(lat1,lon1,lat5,lon5),(lat1,lon1,lat6,lon6) 等等。

非常感谢您提供的任何帮助

【问题讨论】:

  • 你在俄罗斯的stackoverflow网站ru.stackoverflow.com/questions/289889/…找到了详细的答案如果你看不懂,问我试着用英文解释
  • 计算两点之间的距离,地理坐标以米为单位

标签: php sql combinations


【解决方案1】:

这在第二次查询中处理距离计算要好得多,而且速度更快,因此不需要 distance() 函数。此外,您的公式似乎不正确。

$buscar = $_POST['b'];
$buscarcarrera = $_POST['c'];
$whatIWant = substr($buscar, strpos($buscar, "Calle") + 5);
$whatIWant2 = substr($buscarcarrera, strpos($buscarcarrera, "Carrera") + 5);
$vacio = "Calle50A";
$vacioc = "Carrera50A";

if (preg_match('/[A-Za-z]/', $whatIWant))
{
	buscar($buscar, "", $buscarcarrera, "");
}
else
{
	buscar($buscar, $vacio, $buscarcarrera, $vacioc);
}

function buscar($b, $exclusion, $buscarcarrera, $exclusion2)
{
	$con = mysql_connect('localhost', 'root', '');
	mysql_select_db('map', $con);
	$sql = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $b . "%' AND calle not in ('$exclusion')", $con);
	$contar = mysql_num_rows($sql);
	if ($contar == 0)
	{
		echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
	}
	else
	{
		while ($row = mysql_fetch_array($sql))
		{
			$nombre = $row['calle'];
			$id = $row['id'];
			$lat = $row['latitud'];
			$lon = $row['longitud'];

			$dLat = '((latitud-'.$lat.')*PI()/180)';
			$dLong = '((longitud-'.$lon.')*PI()/180)';
			$a = '(sin('.$dLat.'/2) * sin('.$dLat.'/2) + cos('.$lat.'*pi()/180) * cos(map_coords_1*pi()/180) * sin('.$dLong.'/2) * sin('.$dLong.'/2))';
			$sub_sql = '2*atan2(sqrt('.$a.'), sqrt(1-'.$a.'))';
			
			$results = mysql_query(
			"SELECT DISTINCT
					id, calle, " . $sub_sql . " AS distance FROM finalmap
			WHERE
					calle LIKE '%" . $buscarcarrera . "%' AND calle not in ('$exclusion2')
			ORDER BY
				distance
			", $con);
			
			if ($results == 0)
			{
				echo "No se han encontrado resultados para '<b>" . $buscarcarrera . "</b>'.";
			}
			else
			{
				while ($row2 = mysql_fetch_array($results)) {
					echo "Calle: " . $row2['calle'] . " Miles - " . $row2['distance']*3956;
					echo "Calle: " . $row2['calle'] . " Kilometers - " . $row2['distance']*6367;
					echo "Calle: " . $row2['calle'] . " Nautical Miles - " . $row2['distance']*3435;
				}
			}
		}
	}
}

【讨论】:

  • MySQL 的距离计算比PHP 慢几个数量级。
【解决方案2】:

我支持this 的回答,但补充一点:

第四:不要在PHP和MySQL中使用自定义编码的haversine公式进行距离计算和排序。 MySQL 长期支持空间函数。此外,在处理 GIS 时,最好迁移到 PostgreSQL + PostGIS 扩展

第五:在按距离排序时利用 MySQL 或任何其他 RDBMS 空间索引。 我怀疑你可以用你的“每一个距离比较”方法编写更有效的代码。

解决方案:查看this 答案并将以下查询应用于您的数据库表:

ALTER TABLE `finalmap` ENGINE=MYISAM; -- changing your storage engine to MyISAM. It supports spatial indexes in MySQL
ALTER TABLE `finalmap` ADD `pt` POINT NOT NULL; -- adding POINT() spatial datatype for zip cetner. Eventually, you may remove the old lat/lng decimal columns
UPDATE `finalmap` SET `pt` = POINT(latitud,longitud);
ALTER TABLE `finalmap` ADD SPATIAL INDEX(`pt`);

然后,假设您在输入中获得一个坐标(比如说 (6.25,-75.6)),您需要与表中的所有记录进行比较并找到最接近的记录,您需要:

SELECT *, ST_Distance_Sphere(`pt`,POINT('6.25','-75.6')) as `dst`
FROM `fnialmap`
ORDER BY `dst`
LIMIT 1;

最后,第六:请用英文命名您的列和变量。将来可能会有其他人支持您的代码,他们必须处理“纬度”和“经度”

【讨论】:

  • 我同意,但欧几里得几何计算不如经典方法精确。 Innodb 现在支持空间索引。
  • 可能在 5.7.4 中,但 5.7 并没有那么广泛。 ST_Distance_Sphere 不使用欧几里得距离计算
【解决方案3】:

您可以取两个表的笛卡尔积,以便找到所有可能的组合。
否则,采取两个循环,如

for(int i=0;i < n; i++)
  for(int j=0; j< m; j++)
    someProcess(a[i][0], a[i][1], b[j][0], b[j][1]);

这将为您提供所有可能的组合。
然后,您的函数应该具有地理编码/反向地理编码的逻辑来计算距离。

我希望这会有所帮助!

【讨论】:

    【解决方案4】:

    您可以轻松地在 SQL 中生成必要的结果集,而无需编写一堆 PHP 代码。

    如果您真正将您希望的结果显示为表格,这也会使事情变得容易得多。我对你实际上想要做什么感到困惑。我的假设是,当纬度完全匹配经度完全匹配时,您希望匹配两个查询。

    更新:我发现 SQL 隐藏在您的 PHP 代码中。像这样的东西应该可以工作:

    "SELECT t1.latitud AS lat1, t1.longitud AS lon1,
        t2.latitud AS lat2, t2.longitud AS lon2
    FROM finalmap AS t1
    INNER JOIN finalmap AS t2 ON t1.latitud=t2.latitud OR t1.longitud=t2.longitud
    WHERE t1.calle LIKE '%" . $b . "%' AND t1.calle not in ('$exclusion')
        AND t2.calle LIKE '%" . $buscarcarrera . "%' AND t2.calle NOT IN ('exclusion2');"
    

    如果您想要来自 SELECT 1 的所有结果,即使 SELECT 2 中没有匹配项,请将 INNER JOIN 更改为 LEFT JOIN

    我还注意到您在返回查询后检查字段是否具有值。您可以在 SQL 查询的 WHERE 子句中进行这些检查,这样您就不会得到任何您实际上不想要的结果。它仅取决于“无值”的外观(NULL?空字符串?等)如何检查。

    【讨论】:

      【解决方案5】:

      您的逻辑完全错误,并且您正在使您正在寻找的东西变得复杂。它比你想象的要简单得多。

      首先:为了显着加快您的脚本和查询以及保护它免受任何攻击,请使用 PDO 准备好的语句和安全协议。 Mysql noneprepared statements 已弃用十多年!!!

      我的经验(25 年的编码经验)表明我永远不会相信进入您服务器的内容,所以我对安全性有点恐惧...... 本教程解释了你需要做的第一个基本步骤,并解释了这个脚本应该是什么(复制所有代码)。

      注意,使用本地 IP 地址而不是 Localhost 将使 mysql 连接速度提高 2 到 5,具体取决于开发平台(灯、wamp 等)

      设置执行参数

      ini_set('memory_limit', '64M'); // memory limit used by script
      set_time_limit(0);              // no time limit
      

      建立您的安全 mysql 连接

      try {
          $con = new PDO('mysql:host=127.0.0.1;dbname=map;charset=UTF8','root','password');
          $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      } catch (PDOException $e) {
          echo 'Connection failed: ' . $e->getMessage();
      }
      

      建立一个二级安全协议来检查它是否真的是一个ajax调用

      $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) AND
          strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
      if (!$isAjax) {
          $user_error = 'Access denied - not an AJAX request from allowed computer...';
          trigger_error($user_error, E_USER_ERROR);
          exit;
      }
      

      第二:使用距离函数INSIDE你的mysql查询为了正确排序你正在寻找的结果。请注意这里使用的 mysql 距离与您当前使用的不同(更快更精确)。 它将为您提供表 1 中的参考点与表 2 中的所有其他参考点之间的距离,以公里为单位,按距离排序(最接近的为第一行)。

      我们假设你从你的 ajax 得到你的结果是这样的:

      if($isAjax){
          $param4 = '%' . $_POST['b']. '%';  // MYSQL LIKE bind param buscar 
          $param5 = '(' . $_POST['c'] . ')'; //MYSQL NOT IN bind param  buscarcarrera
      }
      

      我们假设您从第一个查询中得到的结果如下(简化):

      $sql1_array = array(
          array('id' => 1, 'Lati' => 6.2523915, 'Longi' => -75.5737028),
          array('id' => 2, 'Lati' => 6.2640349, 'Longi' => -75.5990783)
      );
      

      (注意:始终使用“lati”和“longi”以避免与“long”等PHP函数发生冲突)

      你准备好的陈述:

      $sql2 = "SELECT *, ( 6371 * acos( cos(radians(?)) 
      * cos(radians(Latitude)) * cos(radians(Longitude) - radians(?)) + 
      sin( radians(?) ) * sin(radians(Latitude))))  AS distance 
      FROM finalmap WHERE calle LIKE ? AND calle NOT IN ? 
      ORDER BY distance LIMIT 10";
      
      $stmt = $con->prepare($sql2);
      

      第三:对每个 query1 结果执行一个循环,以获得与表 2 相比的距离。在循环中,您将参数绑定到准备好的 stmt。这将大大加快您的查询速度,因为与数据库的连接是持久的,并且查询是使用 PDO 准备的(已编译并在内存中)。

      foreach ($sql1_array as $key => $value) {
          $stmt->bindParam(1, $value['Lati'], PDO::PARAM_INT);
          $stmt->bindParam(2, $value['Longi'], PDO::PARAM_INT);
          $stmt->bindParam(3, $value['Lati'], PDO::PARAM_INT);
          $stmt->bindParam(4, $param4, PDO::PARAM_STR);
          $stmt->bindParam(5, $param5, PDO::PARAM_STR);
          $stmt->execute();
          $count = $stmt->rowCount();
          if ($count >= 1) {
              $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
            }
      }
      
      
      /*    $result ordered by distance     */
      
      |**************************************|
      |field 0 | field 1 | field 2 | distance|
      |**************************************|
      | 1      | a1      | a2      | 0.00123 | 1 meters
      | 2      | b1      | b2      | 0.01202 | 12 meters
      | 3      | c1      | c2      | 0.23453 | 234 meters
      | 4      | d1      | d2      | 1.58741 | 1km and 587 meters
      |**************************************|  
      

      我想你们都准备好了!享受吧。

      【讨论】:

      • 我不会真正将HTTP_X_REQUESTED_WITH 称为安全协议。它绝对没有任何保障,并给人一种虚假的保护感。
      • 我同意。没有什么是对 tttp 请求的防弹。看他的方法,我只是想解释基本概念。
      • 考虑到您介绍自己“对安全性感到恐惧”和“防止攻击”,感觉不仅仅是基础知识。添加标题检查还不错,但是像安全检查一样解释它。您从never ever trust what s coming into your server 开始,然后使用进入服务器的内容作为安全检查。如果帖子的其余部分不好,我会投反对票。
      【解决方案6】:

      你应该这样做:

      function buscar($b, $exclusion, $buscarcarrera, $exclusion2)
      {
          $nombre1 = array();
          $id1= array();
          $lat1= array();
          $lon1= array();
          $nombre2 = array();
          $id2= array();
          $lat2= array();
          $lon2= array();
      
          $con = mysql_connect('localhost', 'root', '');
          mysql_select_db('map', $con);
          $sql = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $b . "%' AND calle not in ('$exclusion')", $con);
          $contar = mysql_num_rows($sql);
          if ($contar == 0)
          {
              echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
              } else {
          while ($row = mysql_fetch_array($sql))
              {
                  if(array_key_exists('calle',$row) && array_key_exists('id',$row) && array_key_exists('latitud',$row) &&  array_key_exists('longitud',$row)) {
                      // ignore data with a missing field
                      $nombre1[] = $row['calle'];
                      $id1[] = $row['id'];
                      $lat1[] = $row['latitud'];
                      $lon1[] = $row['longitud'];
                  }
              }
          }
      
          $sql2 = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $buscarcarrera . "%' AND calle not in ('$exclusion2')", $con);
          $contar2 = mysql_num_rows($sql2);
          if ($contar2 == 0)
          {
              echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
          } else {
              while ($row2 = mysql_fetch_array($sql2))
              {
                  if(array_key_exists('calle',$row2) && array_key_exists('id',$row2) && array_key_exists('latitud',$row2) &&  array_key_exists('longitud',$row2)) {
                      // ignore data with a missing field
                      $nombre2[] = $row2['calle'];
                      $id2[] = $row2['id'];
                      $lat2[] = $row2['latitud'];
                      $lon2[] = $row2['longitud'];
                  }
              }
          }
          $nbData1 = count($nombre1);
          $nbData2 = count($nombre2);
          if($nbData1 > 0 && $nbData2 > 0) {
              // there are data in both 1st and 2nd query
              $distances = array();
              // loop on all data from 1st query
              for($i1 = 0; $i1 < $nbData1; $i1++) { 
                  // loop on all data from 2nd query
                  for($i2 = $i1; $i2 < $nbData2; $i2++) {
                      // storing result of the distance
                      $distances[] = array('i1' => $i1, 
                                          'i2' => $i2, 
                                          'dist' => array('M' => distance($lat1[$i1], $lon1[$i1], $lat2[$i2], $lon2[$i2], 'M'),
                                                          'K' => distance($lat1[$i1], $lon1[$i1], $lat2[$i2], $lon2[$i2], 'K'),
                                                          'N' => distance($lat1[$i1], $lon1[$i1], $lat2[$i2], $lon2[$i2], 'N')
                                                    )
                                     )
                  }
              }
          }
      
          foreach($distances as $distance) {
              // use each result
              $i1 = $distance['i1'];
              $lat1 = $distance['lat1'];
              $lon1 = $distance['lon1'];
              $i2 = $distance['i1'];
              $lat2 = $distance['lat2'];
              $lon2 = $distance['lon2'];
              $dist = $distance['dist'];
              $distM = $dist['M'];
              $distK = $dist['K'];
              $distN = $dist['N'];
              echo "Have fun with theses data !\n";
          }
      }
      

      因此,存储行的内容。
      然后解析所有数据,计算所有可能的距离。
      然后根据需要使用结果。

      【讨论】:

        【解决方案7】:


        我认为 INNER JOIN 会起作用。我只是很困惑您是否会提供 table-1 的 long 和 lat 值。
        所以我正在编写这两个选项。
        假设第一个表是 table1,第二个是 table2

        1. 何时提供表 1 的经纬度值
          SELECT * FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.latitude=t2.latitud OR t2.longitude=t1.longitude WHERE t1.latitude='#enter the value#' AND t1.longitude='#enter the value#'
        2. 它将提供两个表中相等值的过滤结果
          SELECT * FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.latitude=t2.latitud OR t2.longitude=t1.longitude 

        如果还需要什么,请告诉我。
        谢谢。

        【讨论】:

          【解决方案8】:

          我认为cross join of table 会做你的工作。

          假设第一个表是 Table1,第二个是 Table2。 所以查询:

          Select Table1.id, Table1.latitud, Table1.longitude, 
                 Table2.id, Table2.latitud, Table2.longitude 
          from
                 Table1 , Table2
          

          这将为您提供所需的所有组合。

          【讨论】:

            【解决方案9】:

            我第一次尝试。

            $output_array = array();
            $i = 0;
            
            while ($row = mysql_fetch_array($sql))
            {
                  $j = 0;
                  while ($row2 = mysql_fetch_array($sql2))
                  {
                       $out_array[$i][$j][0] = $row2['latitud'] - $row['latitud']
                       $out_array[$i][$j][1] = $row2['longitud'] - $row['longitud']
                       ++$j;
                  }
                  ++$i;
            }
            print "<pre>";
            print_r($out_array);
            print "</pre>";
            

            预览(随机数):

             Array
            (
            [0] => Array
                (
                    [0] => Array
                        (
                            [0] => 6
                            [1] => 9
                        )
            
                    [1] => Array
                        (
                            [0] => 6
                            [1] => 9
                        )
            
                    [2] => Array
                        (
                            [0] => 6
                            [1] => 9
                        )
                     ............
                     ............
                     ............
            

            Array[0][0][0] == 第一个结果纬度之间的 差异(表 1)AND 第一个结果纬度(表 2)

            Array[0][1][0] == difference 纬度(1st res table1)AND(2nd res table2)

            经度也一样。

            【讨论】:

              猜你喜欢
              • 2013-06-16
              • 1970-01-01
              • 2020-07-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多