Целочисленное поле MySQL возвращается как строка в PHP

127

У меня есть поле таблицы в базе данных MySQL:

userid INT(11)

Поэтому я вызываю его на свою страницу с таким запросом:

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

Затем для обработки результата я делаю:

$row=$result->fetch_assoc();

$id=$row['userid'];

Теперь, если я это сделаю:

echo gettype($id);

Получаю шнурок. Разве это не должно быть целым числом?

Luca
источник
2
запрос MySQL дает значения строк, а не типы строк или любую другую связанную информацию. Только необработанные данные в каждой ячейке таблицы. Вы должны будете учесть это в PHP
Дэн Хэнли,
Если вам нужны типы столбцов, см. Здесь forum.whirlpool.net.au/archive/526795
RichardTheKiwi
Для читателей выбранный ответ предполагает повторение результатов. Но есть решение получше. stackoverflow.com/a/1197424/5079380
Амр ЭльАдави
1
@DanHanly - технически запрос MYSQL (в PHP) возвращает строковое представление значения ячейки - фактическое значение ячейки, хранящееся в базе данных для 32-битного целого числа, является ... 32-битным целым числом, а не строкой цифр ,
ToolmakerSteve
для меня, когда я использовал, $conn->query("your query")я получил целочисленные поля в виде строки, но когда я использовал, $conn->prepare("your query")я получил параметры, как они были в базе данных
Bar Tzadok

Ответы:

129

Когда вы выбираете данные из базы данных MySQL с помощью PHP, тип данных всегда будет преобразован в строку. Вы можете преобразовать его обратно в целое число, используя следующий код:

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

Или с помощью функции intval():

$id = intval($row['userid']);
Мишель Патер
источник
79
Придя к MySQL из опыта работы с Postgres, я чувствую необходимость сказать, что это огромная заноза в заднице. Когда вы выбираете строку в Postgres, вы можете быть уверены, что элементы в массиве строк имеют соответствующие типы данных. Теперь мне нужно написать код, чтобы вручную привести каждый элемент к ожидаемому типу данных.
GordonM
25
Я только что провел несколько тестов в Windows с Laravel и Mysql на той же схеме и на сервере базы данных. В Windows первичный ключ возвращается как целое число, а в Linux - как строка.
AturSams
Хорошо, это хорошая идея, но что, если вам нужно получить тысячи полей? Этот процесс занимает некоторое время .. В моем примере я получаю количество просмотров для каждого сообщения, затем я печатаю все свои сообщения в таблице, я разрешаю пользователю сортировать по View asc и desc, но сортирую от «1» до «9». »или« 9 »на« 1 », поэтому на первом месте могут быть« 9999 »,« 91 »,« 8111 »,« 7 »и т. д.
KeizerBridge
2
Каждое возвращаемое поле будет строкой ИЛИ NULL .
Liam
1
Итак, почему я получаю значения int для столбцов int (я использую подготовленные операторы и тестирую их в Windows)? !
Бухгалтер م
73

Используйте mysqlnd (собственный драйвер) для php.

Если вы используете Ubuntu:

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

Если вы используете Centos:

sudo yum install php-mysqlnd
sudo service httpd restart

Собственный драйвер соответствующим образом возвращает целочисленные типы.

Редактировать:

Как отметил @Jeroen, этот метод будет работать только для PDO из коробки.
Как указал @LarsMoelleken, этот метод будет работать с mysqli, если вы также установите для параметра MYSQLI_OPT_INT_AND_FLOAT_NATIVE значение true.

Пример:

$mysqli = mysqli_init();
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
advait
источник
Я не вижу никаких доказательств этого утверждения. При запуске с mysqlnd и mysqli возвращаются явно строковые значения.
Jeroen
@Jeroen ты уверен? Доступно множество документации. stackoverflow.com/questions/1197005/…
advait
да. Также посмотрите комментарии ниже. Вы сами это проверяли? как сейчас кажется, даже mysqlnd возвращает соответствующие типы, только если вы используете подготовленные операторы. пример: $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);возвращает: array(1) { ["CAST('3.51' AS DECIMAL(3,2))"]=> string(4) "3.51" }
Jeroen
1
Не насколько я понимаю, он должен работать одинаково с PDO и Mysqli. Даже по ссылке, которую вы публикуете выше в комментарии, четко указано, что она РАБОТАЕТ ТОЛЬКО С ПОДГОТОВЛЕННЫМИ ЗАЯВЛЕНИЯМИ, а не с встроенными запросами в соответствии с OP. цитата с этой страницы: Но это только PHP 5.3 (при условии, что ваша версия PHP 5.3 скомпилирована с mysqlnd (а не старой libmysql)), и, похоже, только для подготовленных операторов :-(
Jeroen
7
вы также можете использовать "MYSQLI_OPT_INT_AND_FLOAT_NATIVE" для mysqli
Ларс Моэллекен
20

Самое простое решение, которое я нашел:

Вы можете заставить json_encode использовать фактические числа для значений, которые выглядят как числа:

json_encode($data, JSON_NUMERIC_CHECK) 

(начиная с PHP 5.3.3).

Или вы можете просто преобразовать свой идентификатор в int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
Larney
источник
1
Использование a json_encodeдля преобразования строки в int, если это возможно, похоже на использование микроскопа для заглушки гвоздей.
LibertyPaul
7
Это нарушит все почтовые индексы и номер телефона. Хороший способ сообщить об ошибках в вашем приложении. Решение ДОЛЖНО учитывать тип данных столбца mysql, а не пытаться угадать значение.
Макс Каттинс
1
Макс Каттинс, ваша точка зрения верна. Однако, если вы можете учесть все типы данных, ожидаемые от БД, тогда это будет идеальный путь.
Ifedi Okonkwo
1
Это причинит вам боль, если у вас когда-нибудь будут какие-то идентификаторы вроде "06". Этот флаг преобразует его в 6. Что неверно, потому что эти идентификаторы должны содержать нули в конце.
Hassan Dad Khan
Это отличное решение для ценностей, созданных UNIX_TIMESTAMP(), например,.
Дэвид
18

Мое решение - передать результат запроса $rsи получить в качестве возврата связанный массив приведенных данных:

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

Пример использования:

$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
источник
2
Отличное решение, за исключением того, что «NULL» также является типом переменной, используемым функцией settype (). Таким образом, ваш код изменяет все значения NULL, возвращаемые базой данных (значения, где gettype () == 'NULL'), в тип 'string' со значением '', 'integer' со значением 0 или тип 'float' со значением 0,0. Вам не нужно вызывать settype, если is_null ($ data [$ i] [$ name]) истинно.
Winter Dragoness
Мне нравится техника вдохновителя, но код может быть проще .
ToolmakerSteve
13

Нет. Независимо от типа данных, определенного в ваших таблицах, драйвер MySQL PHP всегда обрабатывает значения строк как строки.

Вам нужно преобразовать свой идентификатор в int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
BoltClock
источник
6
За такое поведение (напрямую) отвечает не PHP, а драйвер базы данных. Если вы взаимодействуете с базой данных Postgres из PHP, вы получите обратно соответствующие типы данных.
GordonM
5

Мне нравится ответ Чада, особенно когда результаты запроса будут переданы в javascript в браузере. Javascript четко обрабатывает числовые объекты как числа, но требует дополнительной работы для работы с числовыми объектами как строками. т.е. должен использовать на них parseInt или parseFloat.

Основываясь на решении Чада, я использую это, и часто это именно то, что мне нужно, и создает структуры, которые могут быть закодированы в JSON для упрощения работы с 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;
    }
}

Добавление числовой строки к 0 дает числовой тип в PHP и правильно определяет тип, поэтому числа с плавающей запятой не будут усечены до целых.

д'Артаньян Эвергрин Барбоза
источник
5

Это происходит, когда PDO::ATTR_EMULATE_PREPARESустановлено trueсоединение.

Однако будьте осторожны: установка этого параметра falseзапрещает использование параметров более одного раза. Я считаю, что это также влияет на качество возвращаемых сообщений об ошибках.

Чарли
источник
2

Вы можете сделать это с помощью ...

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

... в зависимости от расширения, которое вы хотите использовать. Первый не рекомендуется, потому что расширение mysql устарело. Третий пока экспериментальный.

Комментарии к этим гиперссылкам хорошо объясняют, как установить тип из простой старой строки в исходный тип в базе данных.

Некоторые фреймворки также абстрагируют это (CodeIgniter предоставляет $this->db->field_data() ).

Вы также можете делать предположения - например, перебирать полученные строки и использовать is_numeric () для каждой из них. Что-то вроде:

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

Это превратит все, что выглядит как число, в единицу ... определенно не идеально.

Чад Хеджкок
источник
Не работает для поплавков. Они проходят тест is_numeric. Сначала вы должны проверить наличие поплавков.
Martin van Driel
@MartinvanDriel или любая строка, содержащая только числа, но должна быть строкой
treeface 06
@MartinvanDriel Попробуйте добавить:if (is_float()) { $value = (float)$value; }
adjwilli
2

В своем проекте я обычно использую внешнюю функцию, которая «фильтрует» полученные данные mysql_fetch_assoc.

Вы можете переименовывать поля в своей таблице, чтобы было интуитивно понятно, какой тип данных хранится.

Например, вы можете добавить специальный суффикс к каждому числовому полю: если useridон, INT(11)вы можете переименовать его, userid_iа если он, UNSIGNED INT(11)вы можете переименовать userid_u. На этом этапе вы можете написать простую функцию PHP, которая принимает на вход ассоциативный массив (полученный с помощью mysql_fetch_assoc) и применяет приведение к «значению», хранящемуся с этими специальными «ключами».

Didax
источник
1

Если используются подготовленные операторы, то там, где это необходимо, тип будет int. Этот код возвращает массив строк, где каждая строка является ассоциативным массивом. Как if fetch_assoc()был вызван для всех строк, но с сохраненной информацией о типе.

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;
}
Фредрик
источник
1

MySQL имеет драйверы для многих других языков, преобразование данных в строку «стандартизирует» данные и предоставляет пользователю возможность преобразовывать значения в int или другие.

Wolfgang
источник
1
Я считаю, что примитивные типы в значительной степени "стандарт"? Если драйвер, ориентированный на конкретный язык, не может удовлетворить потребности в этом языке, я бы сказал, что это очень разочаровывает
Чанг
1

В моем случае mysqlnd.soрасширение было установлено. НО я этого не сделал pdo_mysqlnd.so. Итак, проблема была решена заменой pdo_mysql.soна pdo_mysqlnd.so.

Антон
источник
0

Мне нравится техника вдохновителя , но код может быть проще:

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

Я также добавил проверку того, что запрос возвращает false, а не набор результатов.
И проверка строки с полем с нулевым значением.
И если желаемый тип - это строка, я не трачу на это время - это уже строка.


Я не использую это в большинстве PHP-кода; Я просто полагаюсь на автоматическое преобразование типов в php. Но при запросе большого количества данных для последующего выполнения арифметических вычислений целесообразно заранее привести к оптимальным типам.

ToolmakerSteve
источник