PHP + PDO + MySQL: jak zwrócić kolumny liczbowe i całkowite z MySQL jako liczby całkowite i liczby w PHP?

85

Widziałem to pytanie powtórzone kilka razy w Stack Overflow, ale żadne z nich w wystarczającym stopniu nie zbadało problemu (lub przynajmniej w sposób, który jest dla mnie pomocny)

Problem polega na tym, że zapytanie DB powinno zwracać w PHP typy danych w postaci liczb całkowitych dla kolumn całkowitych. Zamiast tego kwerenda zwraca każdą kolumnę jako typ ciągu.

Zapewniłem, że „PDO :: ATTR_STRINGIFY_FETCHES” jest fałszywe, aby upewnić się, że wyniki nie są rzutowane na łańcuch.

Odpowiedzi, które widziałem:

  • Nie da się tego zrobić
    • Nie, to działa na Mac OS X z zainstalowanym PHP / MySQL
  • Wpisz rzutuj wszystkie wartości w kodzie
    • Nie, nie będę tego robić
  • Nie martw się, PHP jest luźno wpisane
    • Moje dane są wyprowadzane w formacie JSON i są używane przez wiele innych usług, niektóre wymagają danych w odpowiednim formacie

Z moich badań wynika, że ​​jest to problem dotyczący implementacji sterownika.

Wiele źródeł twierdzi, że natywny sterownik MySQL nie obsługuje zwracania typów liczbowych. Nie wydaje się to prawdą, ponieważ działa w systemie Mac OS X. Chyba że mają na myśli stwierdzenie, że „natywny sterownik MySQL w systemie Linux nie obsługuje tej funkcji”.

Oznacza to, że jest coś szczególnego w sterowniku / środowisku, które zainstalowałem w systemie Mac OS X. Próbowałem zidentyfikować różnice, aby zastosować poprawkę, ale jestem ograniczona wiedzą, jak to sprawdzić.

Różnice:

  • PHP na OS X zostało skompilowane i zainstalowane przez Home Brew
  • PHP na Ubuntu zostało zainstalowane przez „apt-get install php5-dev”
  • PHP na OS X łączy się z serwerem MySQL również działającym na OS X.
    • Wersja serwera: 5.1.71-log Dystrybucja źródła
  • PHP na Ubuntu łączy się z bazą danych w chmurze Rackspace
    • Wersja serwera: 5.1.66-0 + squeeze1 (Debian)

Środowisko Ubuntu

  • Wersja: 10.04.1
  • PHP 5.4.21-1 + debphp.org ~ lucid + 1 (cli) (zbudowano: 21 października 2013 08:14:37)
  • php -i

    pdo_mysql

    Sterownik PDO dla MySQL => włączona wersja Client API => 5.1.72

Środowisko Mac OS X

  • 10.7.5
  • PHP 5.4.16 (cli) (zbudowano: 22 sierpnia 2013 09:05:58)
  • php -i

    pdo_mysql

    Sterownik PDO dla MySQL => włączona wersja Client API => mysqlnd 5.0.10 - 20111026 - $ Id: e707c415db32080b3752b232487a435ee0372157 $

Używane flagi PDO

PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,

Będziemy wdzięczni za wszelką pomoc i wiedzę :) Na pewno wrócę tutaj, jeśli znajdę odpowiedź.

stephenfrank
źródło
2
To odpowiedzialność mysqlnd. Wierzę
Twój zdrowy rozsądek
Mam dokładnie to samo pytanie, ale dotyczy MS SQL Server. Czy istnieje do tego silnie wpisana biblioteka, podobna do mysqlnd? Te najnowsze sterowniki mogę znaleźć zwrócić wszystkie sznurki.
GSerg,

Odpowiedzi:

121

Rozwiązaniem jest upewnienie się, że używasz sterownika mysqlnd dla php.

Skąd wiesz, że nie używasz mysqlnd?

Podczas oglądania php -i, nie będzie bez wzmianki o „mysqlnd”. pdo_mysqlOdcinek będzie miał coś takiego:

pdo_mysql

PDO Driver for MySQL => enabled Client API version => 5.1.72

Jak to zainstalować?

Większość przewodników instalacyjne dla L / A / M / P sugerują apt-get install php5-mysqljednak natywny sterownik MySQL jest instalowany przez inny pakiet: php5-mysqlnd. Okazało się, że było to dostępne z ppa: ondrej / php5-oldstable .

Aby przełączyć się na nowy sterownik (w systemie Ubuntu):

  • Usuń stary sterownik:
    apt-get remove php5-mysql
  • Zainstaluj nowy sterownik:
    apt-get install php5-mysqlnd
  • Zrestartuj Apache2:
    service apache2 restart

Jak mogę sprawdzić, czy sterownik jest używany?

Teraz php -iw pdo_mysqlsekcji wyraźnie wymienimy „mysqlnd” :

pdo_mysql

PDO Driver for MySQL => enabled
Client API version => mysqlnd 5.0.10 - 20111026 - $Id:      e707c415db32080b3752b232487a435ee0372157 $

Ustawienia PDO

Upewnij się, że PDO::ATTR_EMULATE_PREPARESjest false(sprawdź ustawienia domyślne lub ustaw je):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Upewnij się, że PDO::ATTR_STRINGIFY_FETCHESjest false(sprawdź ustawienia domyślne lub ustaw je):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

Zwracane wartości

  • Typy zmiennoprzecinkowe (FLOAT, DOUBLE) są zwracane jako zmiennoprzecinkowe PHP.
  • Typy całkowite (INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT †) są zwracane jako liczby całkowite PHP.
  • Typy ze stałym punktem (DECIMAL, NUMERIC) są zwracane jako ciągi.

† BIGINT o wartości większej niż 64-bitowa liczba int ze znakiem (9223372036854775807) zostanie zwrócona jako ciąg (lub 32 bity w systemie 32-bitowym)

    object(stdClass)[915]
      public 'integer_col' => int 1
      public 'double_col' => float 1.55
      public 'float_col' => float 1.5
      public 'decimal_col' => string '1.20' (length=4)
      public 'bigint_col' => string '18446744073709551615' (length=20)
stephenfrank
źródło
3
Zauważ, że nawet z włączoną tą wersją mysqlnd, DECIMALS nadal będą wysyłane jako ciągi ... (chociaż FLOATS zostaną zwrócone jako zmiennoprzecinkowe!): / Source: Testowałem to.
Pere
4
Zauważ też, że PDO :: ATTR_STRINGIFY_FETCHES nie ma z tym nic wspólnego - próbowałem i nie ma to wpływu na wynik, aw tym poście użytkownik @Josh Davis mówi, że nie ma to związku z MySQL.
Pere
1
Dzięki, zbadałem dokładniej i zaktualizowałem o uwagi dotyczące typu DECIMAL.
stephenfrank
4
@ wcześniej jest to właściwe zachowanie. DECIMAL nie jest typem liczbowym. To ciąg znaków dla liczb. Możesz więc zwrócić: 0.30jak "0.30"zamiast 0.3. Do tego używane są
liczby
dlaczego to cholerny kompromis? Muszę PDO::ATTR_EMULATE_PREPARESużyć nazwanego parametru więcej niż raz
Gaby_64
4

Ta zaakceptowana odpowiedź działa i wydaje się być najpopularniejszą odpowiedzią w Google na to pytanie. Mój problem polega na tym, że muszę wdrożyć aplikację w wielu środowiskach i nie zawsze mam możliwość zainstalowania odpowiedniego sterownika, a ponadto potrzebuję liczb dziesiętnych, aby były liczbami, a nie ciągami. Dlatego stworzyłem procedurę wpisywania wielkości liter przed kodowaniem JSON, którą można łatwo zmodyfikować. To trochę jak zbombardowanie go z orbity.

Najpierw użyj polecenia „pokaż kolumny z”, aby pobrać kolumny z tabeli. mysql query „SHOW COLUMNS FROM table like 'colmunname'”: pytania

$query = 'SHOW COLUMNS FROM ' . $table; //run with mysqli or PDO 

Następnie przenieś typy do tablicy indeksowanej według nazwy kolumny, aby ułatwić iterację. Zakłada, że ​​zestaw wyników z pokazu kolumn znajduje się w zmiennej o nazwie $ columns_from_table. http://php.net/manual/en/function.array-column.php

$column_types = array_column( $columns_from_table, 'Type', 'Field');

Następnie chcemy usunąć nawias z typu, który będzie czymś w rodzaju varchar (32) lub decimal (14,6)

foreach( $column_types as $col=>$type )
{
    $len = strpos( $type, '(' );
    if( $len !== false )
    {
        $column_types[ $col ] = substr( $type, 0, $len );
    }
}

Teraz mamy skojarzoną tablicę z nazwą kolumny jako indeksem i sformatowanym typem jako wartością, na przykład:

Array
(
    [id] => int
    [name] => varchar
    [balance] => decimal
    ...
)

Teraz, gdy dokonasz wyboru z tabeli, możesz iterować wyniki i rzutować wartość na odpowiedni typ:

foreach( $results as $index=>$row )
{
    foreach( $row as $col=>$value )
    {
        switch( $column_types[$col] )
        {
            case 'decimal':
            case 'numeric':
            case 'float':
            case 'double':

                $row[ $col ] = (float)$value;
                break;

            case 'integer':
            case 'int':
            case 'bigint':
            case 'mediumint':
            case 'tinyint':
            case 'smallint':

                $row[ $col ] = (int)$value;
                break;
        }

    }
    $results[ $index ] = $row;
}

Instrukcja switch może być łatwo modyfikowana i może zawierać funkcje daty itp. Na przykład w moim przypadku strona trzecia wypycha wartości walut do bazy danych jako ułamki dziesiętne, ale kiedy pobieram te dane i muszę je zwrócić jako JSON, potrzebuję, aby były to liczby, a nie ciągi.

Testowane z PHP 7, 7.2 i JQuery 2, 3.

Craig Jacobs
źródło