Pole liczby całkowitej MySQL jest zwracane jako łańcuch w PHP

127

Mam pole tabeli w bazie danych MySQL:

userid INT(11)

Więc wzywam go do mojej strony za pomocą tego zapytania:

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

Następnie, aby zająć się wynikiem, robię:

$row=$result->fetch_assoc();

$id=$row['userid'];

Teraz, jeśli to zrobię:

echo gettype($id);

Dostaję sznurek. Czy nie powinna to być liczba całkowita?

luca
źródło
2
zapytanie MySQL podaje wartości wierszy, a nie typy wierszy ani inne powiązane informacje. Tylko nieprzetworzone dane znajdujące się w każdej komórce tabeli. Będziesz musiał to uwzględnić w PHP
Dan Hanly,
Jeśli potrzebujesz typów kolumn, zobacz tutaj forums.whirlpool.net.au/archive/526795
RichardTheKiwi
Dla czytelników wybrana odpowiedź polega na iteracji wyników. Ale jest lepsze rozwiązanie. stackoverflow.com/a/1197424/5079380
Amr ElAdawy
1
@DanHanly - technicznie zapytanie MYSQL (w PHP) zwraca ciąg znaków reprezentujący wartość komórki - rzeczywista wartość komórki przechowywana w bazie danych dla 32-bitowej liczby całkowitej to ... 32-bitowa liczba całkowita, a nie ciąg cyfr .
ToolmakerSteve
dla mnie, kiedy użyłem, $conn->query("your query")otrzymałem pola liczb całkowitych jako ciąg, ale kiedy użyłem, $conn->prepare("your query")otrzymałem parametry tak, jak były w bazie danych
Bar Tzadok

Odpowiedzi:

129

Gdy wybierzesz dane z bazy danych MySQL przy użyciu PHP, typ danych będzie zawsze konwertowany na ciąg. Możesz przekonwertować go z powrotem na liczbę całkowitą, używając następującego kodu:

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

Lub używając funkcji intval():

$id = intval($row['userid']);
Michiel Pater
źródło
79
Przychodząc do MySQL z zaplecza Postgres, czuję, że muszę powiedzieć, że jest to ogromny ból w dupie. Kiedy pobierasz wiersz w Postgres, możesz mieć pewność, że elementy tablicy wierszy mają odpowiednie typy danych. Teraz muszę napisać kod, aby ręcznie rzutować każdy element na oczekiwany typ danych.
GordonM
25
Właśnie wykonałem kilka testów w systemie Windows z Laravel i Mysql na tym samym schemacie i serwerze bazy danych. W systemie Windows klucz podstawowy jest zwracany jako liczba całkowita, aw systemie Linux jest to ciąg.
AturSams
Ok, to dobry pomysł, ale co, jeśli masz tysiące pól do pobrania? Ten proces może chwilę potrwać. W moim przykładzie pobieram liczbę wyświetleń dla każdego posta, następnie drukuję wszystkie moje posty w tabeli, pozwalam użytkownikowi sortować według widoku rosnąco i malejąco, ale sortowanie według liczby od „1” do „9” „lub„ 9 ”na„ 1 ”, więc na pierwszym miejscu możesz mieć„ 9999 ”,„ 91 ”,„ 8111 ”,„ 7 ”itd.
KeizerBridge
@zehelvion Czy kiedykolwiek znalazłeś rozwiązanie tego problemu?
Felipe Francisco
2
Każde zwrócone pole będzie ciągiem LUB wartością NULL .
Liam
73

Użyj mysqlnd (natywnego sterownika) dla php.

Jeśli korzystasz z Ubuntu:

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

Jeśli jesteś na Centos:

sudo yum install php-mysqlnd
sudo service httpd restart

Natywny sterownik odpowiednio zwraca typy całkowite.

Edytować:

Jak zauważył @Jeroen, ta metoda będzie działać tylko po wyjęciu z pudełka w przypadku PDO.
Jak zauważył @LarsMoelleken, ta metoda będzie działać z mysqli, jeśli ustawisz również opcję MYSQLI_OPT_INT_AND_FLOAT_NATIVE na true.

Przykład:

$mysqli = mysqli_init();
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
advait
źródło
Nie widzę żadnego dowodu na to stament. Działając z mysqlnd i mysqli, zwracane są wyraźnie wartości ciągów.
Jeroen,
@Jeroen czy na pewno? Dostępnych jest mnóstwo dokumentacji. stackoverflow.com/questions/1197005/…
advait
tak. Spójrz także na poniższe komentarze. Czy sam to przetestowałeś? jak to wygląda teraz, nawet mysqlnd zwraca tylko odpowiednie typy, jeśli używasz przygotowanych instrukcji. przykład: $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);zwraca: array(1) { ["CAST('3.51' AS DECIMAL(3,2))"]=> string(4) "3.51" }
Jeroen
1
Nie tak, jak to rozumiem, powinno działać na równi z PDO i Mysqli. Nawet w linku, który zamieścisz powyżej w komentarzu, jest jasno określone, że DZIAŁA WYŁĄCZNIE Z PRZYGOTOWANYMI OŚWIADCZENIAMI, a nie z zapytaniami wbudowanymi zgodnie z OP. cytat z tej strony: Ale to jest tylko PHP 5.3 (pod warunkiem, że twoja wersja PHP 5.3 jest skompilowana z mysqlnd (a nie starą biblioteką libmysql)) i wydaje się, że dotyczy to tylko przygotowanych instrukcji :-(
Jeroen
7
możesz również użyć „MYSQLI_OPT_INT_AND_FLOAT_NATIVE” dla mysqli
Lars Moelleken
20

Najłatwiejsze rozwiązanie, które znalazłem:

Możesz wymusić na json_encode używanie rzeczywistych liczb dla wartości, które wyglądają jak liczby:

json_encode($data, JSON_NUMERIC_CHECK) 

(od PHP 5.3.3).

Możesz też po prostu przesłać swój identyfikator do int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
Larney
źródło
1
Stosując json_encodedo oddania ciąg do int czy możliwe jest jak za pomocą mikroskopu dusić paznokcie.
LibertyPaul
7
Spowoduje to uszkodzenie wszystkich kodów pocztowych i numeru telefonu. Dobry sposób na wprowadzenie błędów do aplikacji. Rozwiązanie MUSI respektować typ danych kolumny mysql, a nie próbować odgadnąć na podstawie wartości.
Max Cuttins,
1
Max Cuttins, twój punkt widzenia jest ważny. Jeśli jednak możesz uwzględnić wszystkie typy danych oczekiwane od bazy danych, byłaby to idealna droga do obrania.
Ifedi Okonkwo
1
Będzie cię to bolało, gdy będziesz mieć czasem identyfikatory typu „06”. Ta flaga zamieni ją na 6. Co jest błędne, ponieważ te identyfikatory powinny zawierać końcowe zera.
Hassan Dad Khan
To świetne rozwiązanie dla wartości tworzonych UNIX_TIMESTAMP()np. Przez.
David
18

Moim rozwiązaniem jest przekazanie wyniku zapytania $rsi pobranie tablicy assoc rzutowanych danych jako zwrotu:

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

Przykładowe użycie:

$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
źródło
2
Świetnym rozwiązaniem poza tym, że „NULL” jest również typem zmiennej używanym przez funkcję settype (). Twój kod zmienia więc wszystkie wartości NULL zwracane przez bazę danych (wartości, w których gettype () == 'NULL') na typ 'string' z wartością '', typ 'integer' z wartością 0 lub typ 'float' z wartością 0.0. Nie musisz wywoływać settype, jeśli is_null ($ data [$ i] [$ name]) jest prawdziwe.
Winter Dragoness,
Podoba mi się technika masterminda, ale kodowanie może być prostsze .
ToolmakerSteve
13

Nie. Niezależnie od typu danych zdefiniowanego w tabelach, sterownik MySQL PHP zawsze obsługuje wartości wierszy jako ciągi.

Musisz przesłać swój identyfikator do int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];
BoltClock
źródło
6
To nie PHP jest odpowiedzialne (bezpośrednio) za to zachowanie, to sterownik bazy danych. Jeśli łączysz się z bazą danych Postgres z PHP, otrzymasz z powrotem odpowiednie typy danych.
GordonM
5

Podoba mi się odpowiedź Chada, zwłaszcza gdy wyniki zapytania będą przekazywane do javascript w przeglądarce. Javascript bezproblemowo traktuje encje numeryczne jako liczby, ale wymaga dodatkowej pracy, aby traktować encje podobne do liczb jako ciągi. tj. musi na nich używać parseInt lub parseFloat.

Opierając się na rozwiązaniu Chada, używam tego i często jest to dokładnie to, czego potrzebuję i tworzy struktury, które można zakodować w formacie JSON, aby ułatwić obsługę w 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;
    }
}

Dodanie ciągu liczbowego do 0 tworzy w PHP typ numeryczny i poprawnie identyfikuje typ, dzięki czemu liczby zmiennoprzecinkowe nie zostaną obcięte do liczb całkowitych.

d'Artagnan Evergreen Barbosa
źródło
5

Dzieje się tak, gdy PDO::ATTR_EMULATE_PREPARESjest ustawione truena połączenie.

Ostrożnie, ustawienie go tak, aby falseuniemożliwić użycie parametrów więcej niż raz. Uważam, że wpływa to również na jakość powracających komunikatów o błędach.

Charlie
źródło
2

Możesz to zrobić za pomocą ...

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

... w zależności od rozszerzenia, którego chcesz użyć. Pierwsza nie jest zalecana, ponieważ rozszerzenie mysql jest przestarzałe. Trzeci jest nadal eksperymentalny.

Komentarze w tych hiperłączach dobrze się spisują, wyjaśniając, jak ustawić typ ze zwykłego starego ciągu na jego oryginalny typ w bazie danych.

Niektóre frameworki również to abstrakcyjne (zapewnia CodeIgniter $this->db->field_data()).

Możesz także zgadywać - na przykład przeglądać wynikowe wiersze i używać na każdym z nich is_numeric () . Coś jak:

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

To zmieniłoby wszystko, co wygląda jak liczba, w jedno ... zdecydowanie nie jest idealne.

Chad Hedgcock
źródło
Nie działa dla pływaków. Zdają test is_numeric. Najpierw należy przetestować pływaki.
Martin van Driel,
@MartinvanDriel lub dowolny ciąg, który zawiera tylko liczby, ale powinien być ciągiem
treeface
@MartinvanDriel Spróbuj dodać:if (is_float()) { $value = (float)$value; }
adjwilli
2

W moim projekcie zwykle używam zewnętrznej funkcji, która „filtruje” pobierane dane mysql_fetch_assoc.

Możesz zmieniać nazwy pól w tabeli, aby intuicyjnie zrozumieć, jaki typ danych jest przechowywany.

Na przykład, możesz dodać specjalny sufiks do każdego pola liczbowego: jeśli useridjest INT(11), możesz zmienić jego nazwę userid_ilub jeśli jest UNSIGNED INT(11), możesz zmienić nazwę userid_u. W tym momencie możesz napisać prostą funkcję PHP, która jako dane wejściowe otrzyma tablicę asocjacyjną (pobieraną za pomocą mysql_fetch_assoc) i zastosować rzutowanie na „wartość” przechowywaną za pomocą tych specjalnych „kluczy”.

Didax
źródło
1

Jeśli używane są przygotowane instrukcje, typem będzie int. Ten kod zwraca tablicę wierszy, gdzie każdy wiersz jest tablicą asocjacyjną. Jak gdyby fetch_assoc()został wywołany dla wszystkich wierszy, ale z zachowanymi informacjami o typie.

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
źródło
1

MySQL ma sterowniki dla wielu innych języków, konwertując dane na łańcuchy „standaryzuje” dane i pozostawia użytkownikowi możliwość rzutowania wartości typu na int lub inne

wolfgang
źródło
1
Uważam, że typy prymitywne są w zasadzie „standardem”? Gdyby sterownik specyficzny dla języka nie mógł spełnić wymagań dla tego języka, powiedziałbym, że jest to bardzo rozczarowujące
Chung
1

W moim przypadku mysqlnd.sozostało zainstalowane rozszerzenie. ALE tego nie zrobiłem pdo_mysqlnd.so. Tak więc, problem rozwiązany został poprzez wymianę pdo_mysql.soz pdo_mysqlnd.so.

Anton
źródło
0

Podoba mi się technika masterminda , ale kodowanie może być prostsze:

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

Dodałem również sprawdzanie, czy zapytanie zwraca false, a nie zestaw wyników.
I sprawdzanie wiersza z polem, które ma wartość null.
A jeśli pożądany typ to string, to nie tracę na to czasu - to już jest string.


Nie zawracam sobie głowy używaniem tego w większości kodu PHP; Po prostu polegam na automatycznej konwersji typu php. Ale w przypadku odpytywania dużej ilości danych, aby następnie wykonać obliczenia arytmetyczne, rozsądne jest rzutowanie z góry na optymalne typy.

ToolmakerSteve
źródło