PHP + PDO + MySQL: ฉันจะส่งคืนคอลัมน์จำนวนเต็มและตัวเลขจาก MySQL เป็นจำนวนเต็มและตัวเลขใน PHP ได้อย่างไร


85

ฉันเคยเห็นคำถามนี้ซ้ำสองสามครั้งใน Stack Overflow แต่ไม่มีการสำรวจปัญหาอย่างเพียงพอ (หรืออย่างน้อยก็ในทางที่เป็นประโยชน์สำหรับฉัน)

ปัญหาคือแบบสอบถาม DB ควรส่งคืนชนิดข้อมูลจำนวนเต็มใน PHP สำหรับคอลัมน์จำนวนเต็ม แต่แบบสอบถามจะส่งกลับทุกคอลัมน์เป็นประเภทสตริง

ฉันมั่นใจว่า "PDO :: ATTR_STRINGIFY_FETCHES" หากเป็นเท็จเพื่อให้แน่ใจว่าผลลัพธ์จะไม่ถูกส่งไปยังสตริง

คำตอบที่ฉันเคยเห็น:

  • มันไม่สามารถทำได้
    • ไม่ทำงานบน MacOS X ที่ติดตั้ง PHP / MySQL
  • พิมพ์ cast ค่าทั้งหมดของคุณในโค้ดของคุณ
    • ไม่ฉันจะไม่ทำอย่างนั้น
  • ไม่ต้องกังวลไปเพราะ PHP พิมพ์แบบหลวม ๆ
    • ข้อมูลของฉันถูกส่งออกเป็น JSON และถูกใช้โดยบริการอื่น ๆ จำนวนมากบางส่วนต้องการข้อมูลในรูปแบบที่ถูกต้อง

จากการวิจัยของฉันฉันเข้าใจว่านี่เป็นปัญหาการใช้งานไดรเวอร์

แหล่งข้อมูลหลายแห่งอ้างว่าไดรเวอร์ MySQL เนทีฟไม่รองรับการส่งคืนประเภทตัวเลข สิ่งนี้ดูเหมือนจะไม่เป็นความจริงเนื่องจากทำงานบน Mac OS X เว้นแต่พวกเขาจะบอกว่า "ไดรเวอร์ MySQL เนทีฟบนLinuxไม่รองรับคุณสมบัตินี้"

นี่หมายความว่ามีบางอย่างพิเศษเกี่ยวกับไดรเวอร์ / สภาพแวดล้อมที่ฉันติดตั้งบน Mac OS X ฉันพยายามระบุความแตกต่างเพื่อใช้การแก้ไข แต่ฉันถูก จำกัด ด้วยความรู้เกี่ยวกับวิธีตรวจสอบสิ่งเหล่านี้

ความแตกต่าง:

  • PHP บน OS X ถูกคอมไพล์และติดตั้งผ่าน Home Brew
  • PHP บน Ubuntu ถูกติดตั้งผ่าน "apt-get install php5-dev"
  • PHP บน OS X กำลังเชื่อมต่อกับเซิร์ฟเวอร์ MySQL ที่ทำงานบน OS X
    • เวอร์ชันเซิร์ฟเวอร์: 5.1.71-log Source distribution
  • PHP บน Ubuntu กำลังเชื่อมต่อกับฐานข้อมูล Rackspace Cloud
    • เวอร์ชันเซิร์ฟเวอร์: 5.1.66-0 + บีบ 1 (Debian)

สภาพแวดล้อมของ Ubuntu

  • เวอร์ชัน: 10.04.1.2
  • PHP 5.4.21-1 + debphp.org ~ lucid + 1 (cli) (สร้าง: 21 ต.ค. 2556 08:14:37 น.)
  • php -i

    pdo_mysql

    ไดร์เวอร์ PDO สำหรับ MySQL => เวอร์ชัน Client API ที่เปิดใช้งาน => 5.1.72

สภาพแวดล้อม Mac OS X

  • 10.7.5
  • PHP 5.4.16 (cli) (สร้าง: 22 สิงหาคม 2556 09:05:58 น.)
  • php -i

    pdo_mysql

    ไดร์เวอร์ PDO สำหรับ MySQL => เวอร์ชัน Client API ที่เปิดใช้งาน => mysqlnd 5.0.10 - 20111026 - $ Id: e707c415db32080b3752b232487a435ee0372157 $

ใช้แฟล็ก 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,

ความช่วยเหลือและความเชี่ยวชาญใด ๆ จะได้รับการชื่นชม :) ฉันจะโพสต์กลับที่นี่อย่างแน่นอนหากพบคำตอบ


2
เป็นความรับผิดชอบของ mysqlnd ฉันเชื่อว่า
สามัญสำนึกของคุณ

ฉันมีคำถามเหมือนกันทุกประการ แต่สำหรับ MS SQL Server มีไลบรารีที่พิมพ์อย่างรุนแรงสำหรับสิ่งนั้นคล้ายกับ mysqlnd หรือไม่ ไดรเวอร์ล่าสุดที่ฉันสามารถหากลับสตริงทั้งหมด
GSerg

คำตอบ:


122

วิธีแก้ปัญหาคือตรวจสอบให้แน่ใจว่าคุณใช้ไดรเวอร์mysqlndสำหรับ php

คุณรู้ได้อย่างไรว่าคุณไม่ได้ใช้ mysqlnd?

เมื่อเปิดดูphp -iจะไม่มีการพูดถึง "mysqlnd" pdo_mysqlส่วนจะมีบางสิ่งบางอย่างเช่นนี้

pdo_mysql

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

คุณจะติดตั้งได้อย่างไร?

ส่วนใหญ่คู่มือการติดตั้งสำหรับ L / A / M / P แนะนำapt-get install php5-mysqlแต่ไดรเวอร์พื้นเมืองสำหรับ MySQL php5-mysqlndการติดตั้งโดยแพคเกจที่แตกต่างกัน: ผมพบว่านี่คือสามารถใช้ได้กับppa: Ondrej / php5-oldstable

ในการเปลี่ยนไปใช้ไดรเวอร์ใหม่ (บน Ubuntu):

  • ลบไดรเวอร์เก่า:
    apt-get remove php5-mysql
  • ติดตั้งไดรเวอร์ใหม่:
    apt-get install php5-mysqlnd
  • รีสตาร์ท apache2:
    service apache2 restart

ฉันจะตรวจสอบได้อย่างไรว่ากำลังใช้ไดรเวอร์อยู่

ตอนนี้php -iจะกล่าวถึง "mysqlnd" อย่างชัดเจนในpdo_mysqlส่วน:

pdo_mysql

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

การตั้งค่า PDO

ตรวจสอบให้แน่ใจว่าPDO::ATTR_EMULATE_PREPARESเป็นfalse(ตรวจสอบค่าเริ่มต้นของคุณหรือตั้งค่า):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

ตรวจสอบให้แน่ใจว่าPDO::ATTR_STRINGIFY_FETCHESเป็นfalse(ตรวจสอบค่าเริ่มต้นของคุณหรือตั้งค่า):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

ค่าที่ส่งคืน

  • ประเภทจุดลอยตัว (FLOAT, DOUBLE) จะถูกส่งกลับเป็น PHP ลอย
  • ประเภทจำนวนเต็ม (INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT †) จะแสดงเป็นจำนวนเต็ม PHP
  • ประเภทจุดคงที่ (DECIMAL, NUMERIC) จะถูกส่งกลับเป็นสตริง

† BIGINT ที่มีค่ามากกว่า int ที่ลงชื่อ 64 บิต (9223372036854775807) จะส่งคืนเป็นสตริง (หรือ 32 บิตในระบบ 32 บิต)

    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)

3
โปรดทราบว่าแม้จะเปิดใช้งาน mysqlnd เวอร์ชันนี้แล้ว DECIMALS จะยังคงถูกส่งเป็นสตริง ... (แม้ว่า FLOATS จะถูกส่งกลับเป็นลอย!): / ที่มา: ฉันทดสอบแล้ว
Pere

4
โปรดทราบว่า PDO :: ATTR_STRINGIFY_FETCHES ไม่มีส่วนเกี่ยวข้องใด ๆ - ฉันลองแล้วและไม่มีผลกับผลลัพธ์และในโพสต์นี้ผู้ใช้ @Josh Davis บอกว่าไม่เกี่ยวข้องกับ MySQL
Pere

1
ขอบคุณฉันได้ตรวจสอบเพิ่มเติมและอัปเดตพร้อมบันทึกเกี่ยวกับประเภท DECIMAL
stephenfrank

4
@ นี่คือพฤติกรรมที่ถูกต้อง DECIMAL ไม่ใช่ประเภทตัวเลข เป็นรูปแบบสตริงสำหรับตัวเลข เพื่อให้คุณสามารถกลับมา0.30เป็น"0.30"แทน0.3แทนทศนิยมถูกใช้สำหรับสิ่งนี้ ... ดังนั้นนี่คือพฤติกรรมที่ถูกต้อง
Max Cuttins

ทำไมถึงต้องประนีประนอม? ฉันต้องการPDO::ATTR_EMULATE_PREPARESใช้พารามิเตอร์ที่มีชื่อมากกว่าหนึ่งครั้ง
Gaby_64

5

คำตอบที่ยอมรับนี้ใช้ได้ผลและดูเหมือนจะเป็นคำตอบที่ได้รับความนิยมมากที่สุดใน Google สำหรับคำถามนี้ ปัญหาของฉันคือฉันต้องปรับใช้แอปพลิเคชันในสภาพแวดล้อมที่หลากหลายและฉันไม่มีความสามารถในการติดตั้งไดรเวอร์ที่ถูกต้องเสมอไปและฉันต้องการให้ทศนิยมเป็นตัวเลขไม่ใช่สตริง ดังนั้นฉันจึงสร้างรูทีนเพื่อพิมพ์ case ก่อนการเข้ารหัส JSON ซึ่งแก้ไขได้ง่ายเพื่อให้เหมาะกับ มันเหมือนกับการไล่มันออกจากวงโคจร

ขั้นแรกให้ใช้ "แสดงคอลัมน์จาก" เพื่อรับคอลัมน์จากตาราง mysql query "SHOW COLUMNS FROM table like 'colmunname'": questions

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

จากนั้นรับประเภทลงในอาร์เรย์ที่จัดทำดัชนีโดยชื่อคอลัมน์เพื่อให้ง่ายต่อการทำซ้ำ ถือว่าผลลัพธ์ที่ตั้งจากคอลัมน์แสดงอยู่ในตัวแปรชื่อ $ column_from_table http://php.net/manual/en/function.array-column.php

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

ต่อไปเราต้องการลบวงเล็บออกจากประเภทซึ่งจะเป็นบางอย่างเช่น varchar (32) หรือทศนิยม (14,6)

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

ตอนนี้เรามีอาร์เรย์ที่เชื่อมโยงกับชื่อคอลัมน์เป็นดัชนีและประเภทที่จัดรูปแบบเป็นค่าตัวอย่างเช่น:

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

ตอนนี้เมื่อคุณเลือกจากตารางของคุณคุณสามารถวนซ้ำผลลัพธ์และโยนค่าเป็นประเภทที่เหมาะสม:

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

คำสั่ง switch สามารถแก้ไขได้อย่างง่ายดายเพื่อให้เหมาะสมและอาจรวมถึงฟังก์ชันวันที่เป็นต้นตัวอย่างเช่นในกรณีของฉันบุคคลที่สามกำลังกดค่าสกุลเงินในฐานข้อมูลเป็นทศนิยม แต่เมื่อฉันคว้าข้อมูลนั้นและต้องการส่งคืนเป็น JSON ฉันต้องการให้เป็นตัวเลขไม่ใช่สตริง

ทดสอบด้วย PHP 7, 7.2 และ JQuery 2, 3

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.