El campo entero de MySQL se devuelve como una cadena en PHP

127

Tengo un campo de tabla en una base de datos MySQL:

userid INT(11)

Entonces lo llamo a mi página con esta consulta:

"SELECT userid FROM DB WHERE name='john'"

Luego, para manejar el resultado, hago:

$row=$result->fetch_assoc();

$id=$row['userid'];

Ahora si lo hago:

echo gettype($id);

Me sale una cuerda. ¿No debería ser esto un número entero?

luca
fuente
2
la consulta MySQL proporciona valores de fila, no tipos de fila ni ninguna otra información asociada. Solo los datos sin procesar que están en cada celda de la tabla. Tendrá que dar cuenta de esto en PHP
Dan Hanly
Si necesita los tipos de columna, vea aquí forums.whirlpool.net.au/archive/526795
RichardTheKiwi
Para los lectores, la respuesta seleccionada implica iterar a través de los resultados. Pero hay una mejor solución. stackoverflow.com/a/1197424/5079380
Amr ElAdawy
1
@DanHanly: técnicamente, la consulta MYSQL (en PHP) devuelve una representación de cadena del valor de celda: el valor de celda real almacenado en la base de datos para un entero de 32 bits es ... un entero de 32 bits, no una cadena de dígitos .
ToolmakerSteve
para mí, cuando lo usé, $conn->query("your query")obtuve los campos enteros como una cadena, pero cuando lo hice, $conn->prepare("your query")obtuve los parámetros tal como estaban en la base de datos
Bar Tzadok

Respuestas:

129

Cuando selecciona datos de una base de datos MySQL usando PHP, el tipo de datos siempre se convertirá en una cadena. Puede convertirlo de nuevo a un entero utilizando el siguiente código:

$id = (int) $row['userid'];

O usando la función intval():

$id = intval($row['userid']);
Michiel Pater
fuente
79
Al haber llegado a MySQL desde un fondo de Postgres, siento la necesidad de decir que esto es un gran dolor en el culo. Cuando buscas una fila en Postgres, puedes asegurarte de que los elementos de la matriz de filas tengan los tipos de datos adecuados. Ahora tengo que escribir código para convertir manualmente cada elemento al tipo de datos que espero.
GordonM
25
Acabo de hacer algunas pruebas en Windows con Laravel y Mysql en el mismo esquema y servidor de base de datos. En Windows, la clave principal se devuelve como un entero y en Linux es una cadena.
AturSams
Ok, es una buena idea, pero ¿qué pasa si tiene miles de campos para recuperar? Este proceso lleva un tiempo. En mi ejemplo, recupero el número de vistas para cada publicación, luego imprimo toda mi publicación en una tabla, permito al usuario ordenar por Ver asc y desc, pero se clasifica por "1" a "9 "o" 9 "a" 1 ", así que en primer lugar podría tener" 9999 "," 91 "," 8111 "," 7 ", etc ...
KeizerBridge
@zehelvion ¿alguna vez has encontrado una solución para esto?
Felipe Francisco
2
Cada campo devuelto será una cadena O NULL .
Liam
73

Utilice mysqlnd (controlador nativo) para php.

Si estás en Ubuntu:

sudo apt-get install php5-mysqlnd
sudo service apache2 restart

Si estás en Centos:

sudo yum install php-mysqlnd
sudo service httpd restart

El controlador nativo devuelve los tipos enteros adecuadamente.

Editar:

Como ha señalado @Jeroen, este método solo funcionará de manera inmediata para PDO.
Como ha señalado @LarsMoelleken, este método funcionará con mysqli si también configura la opción MYSQLI_OPT_INT_AND_FLOAT_NATIVE en true.

Ejemplo:

$mysqli = mysqli_init();
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
Advait
fuente
No veo ninguna prueba de esta declaración. Al ejecutarse con mysqlnd y mysqli, se devuelven claramente los valores de cadena.
Jeroen
@Jeroen, ¿estás seguro? Hay mucha documentación disponible. stackoverflow.com/questions/1197005/…
advait
si. También mira los comentarios a continuación. ¿Probaste esto tú mismo? como se ve ahora, incluso mysqlnd solo devuelve los tipos apropiados si usa declaraciones preparadas. ejemplo: $link = mysqli_connect('localhost', 'root', ''); mysqli_set_charset($link,'utf8'); $result = mysqli_query($link,'SELECT CAST(\'3.51\' AS DECIMAL(3,2))'); $row = mysqli_fetch_assoc($result); var_dump($row);devuelve: array(1) { ["CAST('3.51' AS DECIMAL(3,2))"]=> string(4) "3.51" }
Jeroen
1
No es como lo entiendo, debería funcionar igualmente con PDO y Mysqli. Incluso en el enlace que publica arriba en el comentario, se afirma claramente que SOLO FUNCIONA CON DECLARACIONES PREPARADAS y no consultas en línea según el OP. cita de esa página: Pero esto es solo PHP 5.3 (siempre que su versión de PHP 5.3 esté compilada con mysqlnd (y no con el antiguo libmysql)), y parece ser solo el caso para las declaraciones preparadas :-(
Jeroen
77
también puede usar "MYSQLI_OPT_INT_AND_FLOAT_NATIVE" para mysqli
Lars Moelleken
20

La solución más fácil que encontré:

Puede obligar a json_encode a usar números reales para valores que se parecen a números:

json_encode($data, JSON_NUMERIC_CHECK) 

(desde PHP 5.3.3).

O simplemente podría enviar su identificación a un int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
Larney
fuente
1
json_encodeSi es posible, usar una cadena para lanzar int es como usar un microscopio para ahogar las uñas.
LibertyPaul
77
Esto romperá todos los códigos postales y números de teléfono. Buena forma de introducir errores en tu aplicación. La solución DEBE respetar el tipo de datos de la columna mysql, no tratar de adivinar el valor.
Max Cuttins
1
Max Cuttins, tu punto es válido. Sin embargo, si puede dar cuenta de todos los tipos de datos esperados de la base de datos, esta sería una ruta perfecta.
Ifedi Okonkwo
1
Esto te hará daño cuando en algún momento tengas identificadores de algún tipo como '06'. Esta bandera lo convertirá a 6. Lo cual está mal porque esos identificadores deberían seguir ceros a la izquierda.
Hassan Dad Khan
Esta es una gran solución para los valores creados por UNIX_TIMESTAMP(), por ejemplo.
David
18

Mi solución es pasar el resultado de la consulta $rsy obtener una matriz asociada de los datos emitidos como el retorno:

function cast_query_results($rs) {
    $fields = mysqli_fetch_fields($rs);
    $data = array();
    $types = array();
    foreach($fields as $field) {
        switch($field->type) {
            case 3:
                $types[$field->name] = 'int';
                break;
            case 4:
                $types[$field->name] = 'float';
                break;
            default:
                $types[$field->name] = 'string';
                break;
        }
    }
    while($row=mysqli_fetch_assoc($rs)) array_push($data,$row);
    for($i=0;$i<count($data);$i++) {
        foreach($types as $name => $type) {
            settype($data[$i][$name], $type);
        }
    }
    return $data;
}

Ejemplo de uso:

$dbconn = mysqli_connect('localhost','user','passwd','tablename');
$rs = mysqli_query($dbconn, "SELECT * FROM Matches");
$matches = cast_query_results($rs);
// $matches is now a assoc array of rows properly casted to ints/floats/strings
mastermind202
fuente
2
Gran solución, excepto 'NULL', también es un tipo de variable utilizado por la función settype () Por lo tanto, su código cambia todos los valores NULL devueltos por la base de datos (valores donde gettype () == 'NULL') en tipo 'cadena' con '' valor, tipo 'entero' con valor 0 o tipo 'flotante' con valor 0.0. No debe llamar a settype si is_null ($ data [$ i] [$ name]) es verdadero.
Winter Dragoness
Me gusta la técnica de la mente maestra, pero la codificación puede ser más simple .
ToolmakerSteve
13

No. Independientemente del tipo de datos definido en sus tablas, el controlador MySQL de PHP siempre sirve valores de fila como cadenas.

Debe enviar su identificación a un int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
BoltClock
fuente
66
No es PHP el responsable (directamente) de este comportamiento, es el controlador de la base de datos. Si está interactuando con una base de datos Postgres de PHP, obtendrá los tipos de datos apropiados.
GordonM
5

Me gusta la respuesta de Chad, especialmente cuando los resultados de la consulta se pasarán a JavaScript en un navegador. Javascript trata limpiamente las entidades numéricas como números, pero requiere un trabajo adicional para tratar entidades numéricas como cadenas. es decir, debe usar parseInt o parseFloat en ellos.

Basándome en la solución de Chad, uso esto y, a menudo, es exactamente lo que necesito, y crea estructuras que pueden codificarse en JSON para facilitar el manejo en JavaScript.

while ($row = $result->fetch_assoc()) {
    // convert numeric looking things to numbers for javascript
    foreach ($row as &$val) {
        if (is_numeric($val))
            $val = $val + 0;
    }
}

Agregar una cadena numérica a 0 produce un tipo numérico en PHP e identifica correctamente el tipo para que los números de coma flotante no se trunqueen en enteros.

d'Artagnan Evergreen Barbosa
fuente
5

Esto sucede cuando PDO::ATTR_EMULATE_PREPARESse establece entrue en la conexión.

Sin embargo, tenga cuidado al configurarlo para que falseno permita el uso de parámetros más de una vez. Creo que también afecta la calidad de los mensajes de error que regresan.

Charlie
fuente
2

Puedes hacer esto con ...

  1. mysql_fetch_field ()
  2. mysqli_result :: fetch_field_direct o
  3. PDOStatement :: getColumnMeta ()

... dependiendo de la extensión que desee usar. El primero no se recomienda porque la extensión mysql está en desuso. El tercero sigue siendo experimental.

Los comentarios en estos hipervínculos hacen un buen trabajo al explicar cómo configurar su tipo de una cadena antigua simple a su tipo original en la base de datos.

Algunos marcos también resumen esto (CodeIgniter proporciona $this->db->field_data()).

También puede hacer conjeturas, como recorrer las filas resultantes y usar is_numeric () en cada una. Algo como:

foreach($result as &$row){
 foreach($row as &$value){
  if(is_numeric($value)){
   $value = (int) $value;
  }       
 }       
}

Esto convertiría cualquier cosa que parezca un número en uno ... definitivamente no es perfecto.

Chad Hedgcock
fuente
No funciona para carrozas. Pasan la prueba is_numeric. Debes probar primero los flotadores.
Martin van Driel
@MartinvanDriel o cualquier cadena que contenga solo números, pero debería ser una cadena
treeface
@MartinvanDriel Intente agregar:if (is_float()) { $value = (float)$value; }
adjwilli
2

En mi proyecto, generalmente uso una función externa que "filtra" los datos recuperados mysql_fetch_assoc.

Puede cambiar el nombre de los campos en su tabla para que sea intuitivo entender qué tipo de datos se almacena.

Por ejemplo, puede agregar un sufijo especial a cada campo numérico: si userides un INT(11)puede cambiarle el nombre userid_io si es un UNSIGNED INT(11)puede cambiarle el nombre userid_u. En este punto, puede escribir una función PHP simple que reciba como entrada la matriz asociativa (recuperada con mysql_fetch_assoc) y aplique la conversión al "valor" almacenado con esas "claves" especiales.

Didax
fuente
1

Si se utilizan declaraciones preparadas, el tipo será int cuando corresponda. Este código devuelve una matriz de filas, donde cada fila es una matriz asociativa. Como si fetch_assoc()se llamara para todas las filas, pero con información de tipo preservada.

function dbQuery($sql) {
    global $mysqli;

    $stmt = $mysqli->prepare($sql);
    $stmt->execute();
    $stmt->store_result();

    $meta = $stmt->result_metadata();
    $params = array();
    $row = array();

    while ($field = $meta->fetch_field()) {
      $params[] = &$row[$field->name];
    }

    call_user_func_array(array($stmt, 'bind_result'), $params);

    while ($stmt->fetch()) {
      $tmp = array();
      foreach ($row as $key => $val) {
        $tmp[$key] = $val;
      }
      $ret[] = $tmp;
    }

    $meta->free();
    $stmt->close();

    return $ret;
}
Fredrik
fuente
1

MySQL tiene controladores para muchos otros idiomas, la conversión de datos a cadenas "estandariza" los datos y deja que el usuario escriba los valores de conversión a int u otros

Wolfgang
fuente
1
¿Creo que los tipos primitivos son más o menos un "estándar"? Si un controlador específico del idioma no pudiera satisfacer las necesidades de ese idioma, diría que es muy decepcionante
Chung
1

En mi caso, la mysqlnd.soextensión había sido instalada. PERO no lo hice pdo_mysqlnd.so. Entonces, el problema se resolvió reemplazando pdo_mysql.socon pdo_mysqlnd.so.

Anton
fuente
0

Me gusta la técnica de la mente maestra , pero la codificación puede ser más simple:

function cast_query_results($result): array
{
    if ($result === false)
      return null;

    $data = array();
    $fields = $result->fetch_fields();
    while ($row = $result->fetch_assoc()) {
      foreach ($fields as $field) {
        $fieldName = $field->name;
        $fieldValue = $row[$fieldName];
        if (!is_null($fieldValue))
            switch ($field->type) {
              case 3:
                $row[$fieldName] = (int)$fieldValue;
                break;
              case 4:
                $row[$fieldName] = (float)$fieldValue;
                break;
              // Add other type conversions as desired.
              // Strings are already strings, so don't need to be touched.
            }
      }
      array_push($data, $row);
    }

    return $data;
}

También agregué la comprobación de la consulta que devuelve falso en lugar de un conjunto de resultados.
Y buscando una fila con un campo que tenga un valor nulo.
Y si el tipo deseado es una cadena, no pierdo el tiempo, ya es una cadena.


No me molesto en usar esto en la mayoría del código php; Solo confío en la conversión automática de tipos de php. Pero si se consultan muchos datos, para luego realizar cálculos aritméticos, es sensato emitir los tipos óptimos por adelantado.

ToolmakerSteve
fuente