O campo inteiro do MySQL é retornado como string no PHP

127

Eu tenho um campo de tabela em um banco de dados MySQL:

userid INT(11)

Então, eu estou chamando para a minha página com esta consulta:

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

Então, para lidar com o resultado, eu faço:

$row=$result->fetch_assoc();

$id=$row['userid'];

Agora se eu fizer:

echo gettype($id);

Eu recebo uma corda. Não deveria ser um número inteiro?

luca
fonte
2
a consulta MySQL fornece valores de linha, não tipos de linha ou qualquer outra informação associada. Apenas os dados brutos que estão em cada célula da tabela. Você vai ter que responder por isso em PHP
Dan Hanly
Se você precisar os tipos de coluna, veja aqui forums.whirlpool.net.au/archive/526795
RichardTheKiwi
Para os leitores, a resposta selecionada envolve a iteração nos resultados. Mas existe uma solução melhor. stackoverflow.com/a/1197424/5079380
Amr ElAdawy
1
@DanHanly - tecnicamente, a consulta MYSQL (em PHP) retorna uma representação em cadeia do valor da célula - o valor real da célula armazenado no banco de dados para um número inteiro de 32 bits é ... um número inteiro de 32 bits, não uma sequência de dígitos .
Home
para mim, quando eu usei $conn->query("your query")eu tenho os campos inteiros como seqüência de caracteres, mas quando eu usei $conn->prepare("your query")eu tenho os parâmetros como eles estavam no banco de dados
Bar Tzadok

Respostas:

129

Quando você seleciona dados de um banco de dados MySQL usando PHP, o tipo de dados sempre será convertido em uma string. Você pode convertê-lo novamente em um número inteiro usando o seguinte código:

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

Ou usando a função intval():

$id = intval($row['userid']);
Michiel Pater
fonte
79
Tendo chegado ao MySQL com experiência no Postgres, sinto a necessidade de dizer que isso é uma enorme dor de cabeça. Ao buscar uma linha no Postgres, você pode verificar se os elementos na matriz de linhas tinham tipos de dados apropriados. Agora estou tendo que escrever um código para converter manualmente todos os elementos no tipo de dados que estou esperando.
GordonM
25
Acabei de fazer alguns testes no Windows com o Laravel e o Mysql no mesmo esquema e servidor de banco de dados. No Windows, a chave primária é retornada como um número inteiro e no Linux é uma string.
AturSams 28/10/14
Ok, é uma boa ideia, mas e se você tiver milhares de campos para recuperar? Esse processo demora um pouco. No meu exemplo, recupero o número de visualizações de cada postagem e, em seguida, imprimo todas as minhas postagens em uma tabela, permitindo que o usuário classifique por View asc e desc, mas classifique por "1" a "9 "ou "9" para "1" assim em primeiro lugar que você poderia ter "9999", "91", "8111", "7", etc ...
KeizerBridge
@zehelvion você já encontrou uma solução para isso?
Felipe Francisco
2
Todo campo retornado será uma string OU NULL .
Liam
73

Use o mysqlnd (driver nativo) para php.

Se você estiver no Ubuntu:

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

Se você estiver no Centos:

sudo yum install php-mysqlnd
sudo service httpd restart

O driver nativo retorna tipos inteiros adequadamente.

Editar:

Como o @Jeroen apontou, esse método só funciona imediatamente para a DOP.
Como o @LarsMoelleken apontou, este método funcionará com o mysqli se você também definir a opção MYSQLI_OPT_INT_AND_FLOAT_NATIVE como true.

Exemplo:

$mysqli = mysqli_init();
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
advait
fonte
Eu não vejo nenhuma prova desse stament. Executando com mysqlnd e mysqli, claramente os valores das strings são retornados.
Jeroen
@Jeroen, você tem certeza? Há muita documentação disponível. stackoverflow.com/questions/1197005/…
advait
sim. Veja também os comentários abaixo. Você testou isso sozinho? como parece agora, até o mysqlnd retornará apenas os tipos apropriados se você usar instruções preparadas. exemplo: $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);retorna: array(1) { ["CAST('3.51' AS DECIMAL(3,2))"]=> string(4) "3.51" }
Jeroen
1
Não é como eu o entendo, ele deve funcionar igualmente com o DOP e o Mysqli. Mesmo no link que você postou acima no comentário, está claramente indicado que FUNCIONA SOMENTE COM DECLARAÇÕES PREPARADAS e não com consultas em linha, conforme o OP. quote from this
Jeroen
7
você também pode usar "MYSQLI_OPT_INT_AND_FLOAT_NATIVE" para mysqli
Lars Moelleken 11/16/16
20

Solução mais fácil que encontrei:

Você pode forçar o json_encode a usar números reais para valores parecidos com números:

json_encode($data, JSON_NUMERIC_CHECK) 

(desde o PHP 5.3.3).

Ou você pode simplesmente converter seu ID para um int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
Larney
fonte
1
Usar uma json_encodecorda para moldar para int, se possível, é como usar um microscópio para sufocar as unhas.
precisa saber é o seguinte
7
Isso quebrará todos os códigos postais e números de telefone. Boa maneira de introduzir erros no seu aplicativo. A solução DEVE respeitar o tipo de dados da coluna mysql, não tentar adivinhar a partir do valor.
Max Cuttins
1
Max Cuttins, seu ponto é válido. No entanto, se você puder dar conta de todos os tipos de dados esperados do banco de dados, esse seria o caminho perfeito a seguir.
precisa saber é o seguinte
1
Isso vai machucá-lo quando você tiver ids de algum tipo como '06'. Esse sinalizador o converterá em 6. O que está errado, porque esses IDs devem ficar à direita de zeros.
Hassan Dad Khan
Essa é uma ótima solução para valores criados por UNIX_TIMESTAMP(), por exemplo.
David
18

Minha solução é passar o resultado da consulta $rse obter uma matriz assoc dos dados convertidos como o 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;
}

Exemplo 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
fonte
2
Ótima solução, exceto 'NULL', também é um tipo de variável usado pela função settype (). Portanto, seu código altera todos os valores NULL retornados pelo banco de dados (valores em que gettype () == 'NULL') em tipo 'string' com '' valor, tipo 'número inteiro' com valor 0 ou tipo 'float' com valor 0,0. Você não deve chamar settype se is_null ($ data [$ i] [$ name]) for verdadeiro.
Winter Dragoness
Eu gosto da técnica do idealizador, mas a codificação pode ser mais simples .
Página
13

Não. Independentemente do tipo de dado definido em suas tabelas, o driver MySQL do PHP sempre serve valores de linha como strings.

Você precisa converter seu ID para um int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
BoltClock
fonte
6
Não é o PHP responsável (diretamente) por esse comportamento, é o driver do banco de dados. Se você estiver interagindo com um banco de dados Postgres a partir do PHP, receberá de volta os tipos de dados apropriados.
GordonM
5

Eu gosto da resposta do Chade, especialmente quando os resultados da consulta serão repassados ​​para javascript em um navegador. O Javascript lida de maneira limpa com entidades semelhantes numéricas como números, mas requer trabalho extra para lidar com entidades semelhantes numéricas como seqüências de caracteres. ou seja, deve usar parseInt ou parseFloat neles.

Com base na solução do Chade, eu uso isso e geralmente é exatamente o que eu preciso e cria estruturas que podem ser codificadas em JSON para facilitar o manuseio em 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;
    }
}

Adicionar uma sequência numérica a 0 produz um tipo numérico no PHP e identifica corretamente o tipo para que os números de ponto flutuante não sejam truncados em números inteiros.

d'Artagnan Evergreen Barbosa
fonte
5

Isso acontece quando PDO::ATTR_EMULATE_PREPARESestá definido como truena conexão.

Cuidado, porém, configurá-lo para falsenão permitir o uso de parâmetros mais de uma vez. Acredito que isso também afeta a qualidade das mensagens de erro retornadas.

Charlie
fonte
2

Você pode fazer isso com ...

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

... dependendo da extensão que você deseja usar. O primeiro não é recomendado porque a extensão mysql está obsoleta. O terceiro ainda é experimental.

Os comentários nesses hiperlinks explicam como definir seu tipo de uma seqüência antiga simples para seu tipo original no banco de dados.

Algumas estruturas também abstraem isso (o CodeIgniter fornece $this->db->field_data()).

Você também pode adivinhar - como percorrer as linhas resultantes e usar is_numeric () em cada uma. Algo como:

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

Isso transformaria qualquer coisa que parecesse um número em um ... definitivamente não é perfeito.

Chad Hedgcock
fonte
Não funciona para carros alegóricos. Eles passam no teste is_numeric. Você deve testar primeiro os carros alegóricos.
Martin van Driel
@MartinvanDriel ou qualquer string que contém apenas números, mas deve ser uma string
treeface
@MartinvanDriel Tente adicionar:if (is_float()) { $value = (float)$value; }
adjwilli 15/09/16
2

No meu projeto, geralmente uso uma função externa que "filtra" os dados recuperados mysql_fetch_assoc.

Você pode renomear campos na sua tabela para que seja intuitivo entender qual tipo de dados está armazenado.

Por exemplo, você pode adicionar um sufixo especial a cada campo numérico: se useridfor um, INT(11)você pode renomeá-lo userid_iou, se for, UNSIGNED INT(11)você pode renomear userid_u. Nesse ponto, você pode escrever uma função PHP simples que receba como entrada a matriz associativa (recuperada com mysql_fetch_assoc) e aplicar a conversão ao "valor" armazenado com essas "chaves" especiais.

Didax
fonte
1

Se declarações preparadas forem usadas, o tipo será int, quando apropriado. Esse código retorna uma matriz de linhas, onde cada linha é uma matriz associativa. Como se fetch_assoc()fosse chamado para todas as linhas, mas com informações de tipo preservadas.

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
fonte
1

O MySQL possui drivers para muitas outras linguagens, converter dados em string "padroniza" dados e deixa ao usuário digitar valores de conversão para int ou outros

wolfgang
fonte
1
Eu acredito que tipos primitivos são praticamente um "padrão"? Se um driver específico do idioma não poderia preencher as necessidades para que a linguagem, eu diria que é muito decepcionante
Chung
1

No meu caso, a mysqlnd.soextensão foi instalada. MAS eu não tinha pdo_mysqlnd.so. Portanto, o problema foi resolvido substituindo-o pdo_mysql.sopor pdo_mysqlnd.so.

Anton
fonte
0

Eu gosto da técnica do idealizador , mas a codificação pode ser mais simples:

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;
}

Também adicionei a verificação de consultas retornando false em vez de um conjunto de resultados.
E verificando uma linha com um campo que tenha um valor nulo.
E se o tipo desejado é uma string, não perco tempo com ela - já é uma string.


Eu não me incomodo em usar isso na maioria dos códigos php; Eu apenas confio na conversão automática de tipos do php. Mas, se você estiver consultando muitos dados, para realizar cálculos aritméticos, é sensato converter os tipos ideais no início.

Fabricante de ferramentas
fonte