2011-08-26 26 views
8

Acabo de empezar a usar MySQL con PHP y me gustaría saber si es posible crear una función personalizada. Este fragmento de código debe ilustrar lo que intento hacer.¿Cómo crear una función MySQL personalizada?

// a somewhat complicated formula not suitable to embed into the query 
function Distance($latA, $lonA, $latB, $lonB) 
{ 
    // earth's radius 
    $radius = 3956; 

    $latA = deg2rad($latA); 
    $lonA = deg2rad($lonA); 
    $latB = deg2rad($latB); 
    $lonB = deg2rad($lonB); 

    // calculate deltas 
    $deltaLat = $latB - $latA; 
    $deltaLon = $lonB - $lonA; 

    // calculate Great Circle distance 
    $result = pow(sin($deltaLatitude/2.0), 2) + (cos($latA) * cos($latB) * pow(sin($deltaLon/2.0), 2)); 
    $distance = $radius * 2 * atan2(sqrt($result), sqrt(1 - $result)); 

    return $distance; 
} 

// how can I call Distance() in my query? 
$query = "SELECT lat, lon FROM zipcodes WHERE Distance(lat, lon, 0, 0) < 20"; 
mysql_query($query); 

¡Gracias de antemano por cualquier ayuda!

+1

Para su información, puede hacer que la hallan perfectamente desde una consulta. De hecho, si usa un índice espacial basado en sus lat/lngs, verá que los resultados son impresionantes. Increíble de hecho. – Layke

+0

No estoy seguro de lo que quieres decir, ¿podrías explicar esto? También por "inadecuado" no quise decir en términos de rendimiento (no sé nada de eso); Quise decir cuán dolorosamente larga sería la consulta. –

Respuesta

12

Usted declara esta función MySQL en su aplicación, y permanecerá en la base de datos hasta que se reinicie el servidor de la base de datos.

mysql_query("CREATE FUNCTION Distance(LAT_A INT, LON_A INT, LAT_B INT, LON_B INT,) 
RETURNS INT 
READS SQL DATA 
DETERMINISTIC 
BEGIN 
DECLARE radius, deltaLat, deltaLon, result, distance BIGINT; 
SET radius=3956; 
SET deltaLat=LAT_B-LAT_A; 
SET deltaLon=LON_B-LON_A; 
SET result=POW(SIN(deltaLat/2), 2) + (COS(LAT_A) * COS(LAT_B) * POW(SIN(deltaLon/2.0), 2)); 
SET distance=radius * 2 * ATAN2(SQRT(result), SQRT(1 - result)); 
RETURN distance; 
END"); 

Esto usa MySQL's mathematical functions. La descarga de este procesamiento a la base de datos es rápida y eficiente (los datos no tienen que viajar a través del cable, y solo se le devuelven los resultados que desea).

Una vez que se ha declarado esto, se puede usar como tal:

$query = "SELECT lat, lon FROM zipcodes WHERE Distance(lat, lon, 0, 0) < 20"; 
mysql_query($query); 

Sin embargo, si su base de datos se reinicia, las funciones o procedimientos declarados previamente se pierden. Es posible manejar correctamente el error 1305 de MySQL (Function functionName does not exist) en el nivel de la aplicación.

En su controlador de errores de base de datos:

switch (mysql_errno()): 
    case 1305: 
     if (false === $database->_declareStoredProcedureFlag) { 
      if ($c = preg_match_all("/FUNCTION [a-zA-Z0-9]+\." . 
       "([a-zA-Z0-9_]*) does not exist/is", 
       mysql_error(), $matches) 
      ) { 
       $storedFunctionName = $matches[1][0]; 
       $database->_declareStoredProcedureFlag = true; 
       if (true === $database->declareStoredFunction($storedFunctionName)) { 
        $result = mysql_query($query); 
       } 
      } 
     } 
     break; 
    ... 
+0

¿Es esta la única opción? Prefiero mantenerlo confinado a PHP en este momento. Esperaré unos minutos para marcar una respuesta. –

+0

yeh Supongo que no está sucediendo. Voy a ir con esto, +1. Salud. –

+1

Como está utilizando los valores 'lat' y' lon' en la llamada a la función 'Distance()' en la consulta, y estos provienen de la base de datos, la única otra opción es seleccionar el conjunto de datos completo (que contiene esos valores) de la tabla en PHP, ejecútelo a través de la versión de PHP de la función y ejecute esencialmente el cálculo de números (qué bases de datos están diseñadas para funcionar de manera eficiente) en PHP (que es significativamente más lento). A continuación, tiene la carga adicional de transferir los datos de la base de datos a PHP. Esta es la solución óptima, pero obviamente agrega complejidad. – Andy

1

Sin duda puede escribir una función MySQL para hacer esto. No tengo tiempo para escribir uno ahora, pero aquí es donde empezar:.

http://dev.mysql.com/doc/refman/5.0/en/create-procedure.html

+0

Vaya, pensé que quería tomar su función php y convertirla en una función MySQL. ¡Lo siento! –

+0

Hubo otra respuesta que mostró cómo llamar a la función usando ". Function_ame()." sintaxis que también es factible si quieres mantener tu función en PHP. –

0

Dado que las funciones no funcionan entre comillas "o" hay que dividirlo con un punto (.)

$query = mysql_query("SELECT lat, lon FROM zipcodes WHERE ".Distance($lat, $lon, 0, 0)." < 20"); 

Espero que esto ayude :)

rutinas
+0

Como @thedaian se indicó anteriormente, esto no funcionará como 'lat' y' lon' son variables en la instrucción SELECT. –

1

MySQL tiene almacenados http://dev.mysql.com/doc/refman/5.0/en/stored-routines.html

Algunas de las funciones pueden ser diferentes, por ejemplo, el mysql equivalente a deg2rad de php es la función radians() de mysql. Creo que deberías poder crear una función equivalente. Con este enfoque, debe tener en cuenta que estas consultas tendrán que mostrar todas las tablas de códigos postales cada vez.

Cuestiones relacionadas