ใช้ ADOdb กับ PHP และ Oracle มีข้อดีเยอะมากมาย (2/2)


หน้าแรก PHP MySQL เกร็ดความรู้ ใช้ ADOdb กับ PHP และ Oracle มีข้อดีเยอะมากมาย (2/2)

6.      In and Out Parameters

PL/SQL stored procedure ต่อไปนี้ต้องการตัวแปรนำเข้า และคืนค่ามาเป็นตัวแปรส่งออก

PROCEDURE data_out(input IN VARCHAR, output OUT VARCHAR) IS

       BEGIN

              output := 'I love '||input;

       END;

ตัวอย่างโปรแกรม ADOdb ต่อไป แสดงการเรียก stored procedure

$stmt = $db->PrepareSP("BEGIN adodb.data_out(:a1, :a2); END;");

$input = 'Sophia Loren';

$db->InParameter($stmt,$input,'a1');

$db->OutParameter($stmt,$output,'a2');

$ok = $db->Execute($stmt);

if ($ok) echo ($output == 'I love Sophia Loren') ? 'OK' : 'Failed';

ฟังก์ชัน PrepareSP(เป็นฟังก์ชันพิเศษที่รู้เกี่ยวกับค่าพารามิเตอร์แบบ bind ซึ่งข้อจำกัดหลักที่มีอยู่ตอนนี้ก็คือ พารามิเตอร์ IN OUT ยังทำงานไม่ได้

Bind Parameters และ REF CURSORs

เราสามารถเขียนตัวอย่าง REF CURSOR ใหม่ โดยใช้ InParameter (ซึ่งต้องใช้กับ ADOdb 4.53 ขึ้นไป) ใหม่ได้ดังนี้

$stmt = $db->PrepareSP("BEGIN adodb.open_tab(:refc,:tabname); END;");

$input = 'A%';

$db->InParameter($stmt,$input,'tabname');

$rs = $db->ExecuteCursor($stmt,'refc');

while ($arr = $rs->FetchRow()) print_r($arr);

Bind Parameters และ LOBs

เรายังสามารถทำงานบน LOBs ดังตัวอย่างต่อไปนี้ เรามีพารามิเตอร์ IN และ OUT ที่เป็น CLOBs

$text = 'test test test';

$sql = "declare rs clob; begin :rs := lobinout(:sa0); end;";

$stmt = $conn -> PrepareSP($sql);

$conn -> InParameter($stmt,$text,'sa0', -1, OCI_B_CLOB); # -1 means variable length

$rs = '';

$conn -> OutParameter($stmt,$rs,'rs', -1, OCI_B_CLOB);

$conn -> Execute($stmt);

echo "return = ".$rs."<br>";

เราสามารถใช้ OCI_B_BLOB ในการระบุว่า เรากำลังใช้ BLOBs ได้ด้วยเช่นกัน

การนำมาใช้ใหม่ของ Bind Parameters กับ CURSOR_SHARING=FORCE

เว็บโปรแกรมเมอร์หลายรายไม่สนใจที่จะใช้พารามิเตอร์แบบ bind และต้องการที่จะใส่ค่าไปใน SQL โดยตรง ดังนั้นแทนที่เราจะเขียนโปรแกรมแบบนี้

$arr = $db->GetArray("select * from emp where empno>:emp", array('emp' =>7900));

ก็สามารถใส่ค่าข้อมูลเข้าไปใน SQL ได้ดังนี้

$arr = $db->GetArray("select * from emp where empno>7900");

การทำเช่นนี้จะลดประสิทธิภาพการทำงานของออราเคิล เพราะว่าออราเคิลจะใช้ SQL ที่ comply แล้วนำมาใช้ใหม่ ซึ่งระบุไปยัง SQL ที่comply ไปก่อนหน้านี้แล้ว ตัวอย่างข้างบนที่มีค่าของข้อมูลอยู่ใน SQL จะไม่ได้ถูกนำมาใช้ใหม่จริง และจากการปรับเปลี่ยนให้เหมาะสม ออราเคิลตั้งแต่เวอร์ชัน 8.1 เป็นต้นไป เราสามารถกำหนดพารามิเตอร์แบบ session ได้หลังจากที่เราเข้าสู่ระบบแล้ว

ALTER SESSION SET CURSOR_SHARING=FORCE

ด้วยการกำหนดแบบนี้ ทำให้ออราเคิลแปลงค่าตัวแปรทั้งหมด (เช่น ค่า 7900) ให้เป็นค่าพารามิเตอร์แบบ bind เพื่อปรับปรุงประสิทธิภาพการนำ SQL มาใช้ใหม่

รายละเอียดเกี่ยวกับ Speedup Tips

7.      Dates and Datetime in ADOdb

ใน ADOdb มีสองสิ่งที่เราจำเป็นต้องทำความเข้าใจเกี่ยวกับข้อมูลวันที่ อย่างแรกก็คือ ต้องมั่นใจว่า ข้อมูลในระบบฐานข้อมูลต่างๆ นั้นใช้ร่วมกันได้ ซึ่ง ADOdb ตั้งสมมติฐานว่า ข้อมูลวันที่จะคืนค่ามาในรูปแบบ ISO นั่นคือ YYYY-MM-DD H24:MI:SS

อย่างที่สองก็คือ เนื่องจากระบบฐานข้อมูลออราเคิลเก็บข้อมูลวันที่และเวลาโดยใช้ชนิดข้อมูลเดียวกัน เราจึงตัดสินใจไม่แสดงค่าเวลาในข้อมูลรูปแบบวันที่ที่เป็นค่าปริยาย ดังนั้นเมื่อเข้าสู่ระบบ ADOdb จะกำหนดค่า NLS_DATE_FORMAT ให้เป็น 'YYYY-MM-DD' ในตัวอย่างต่อไปนี้ จะเป็นการแสดงค่าวันที่ และเวลาที่เป็นค่าปริยาย

$db = NewADOConnection('oci8');

$db->NLS_DATE_FORMAT =  'RRRR-MM-DD HH24:MI:SS';

$db->Connect($tns, $user, $pwd);

หรือจะสั่งโดย

$sql = "ALTER SESSION SET NLS_DATE_FORMAT = 'RRRR-MM-DD HH24:MI:SS'";

$db->Execute($sql);

ถ้าเราไม่สนใจเรื่องวันที่ที่เป็น Portability และไม่ได้ใช้ portability layer ของ ADOdb เราสามารถใช้รูปแบบวันที่ที่เราต้องการได้เลย

8.      Database Portability Layer

ADOdb มีฟังก์ชันสำหรับช่วยสร้างฟังก์ชัน SQL ที่คืนค่ามาเป็น string ให้เราสามารถนำมารวมเข้ากับชุดคำสั่ง SQL ของเราเองได้ด้วย

ฟังก์ชัน

คำอธิบาย

DBDate($date)

ส่งค่าเวลาในยูนิกซ์ หรือวันที่แบบ ISOฟังก์ชันจะแปลงให้เป็นข้อมูลวันที่ในรูปแบบstring เพื่อใช้ในการ INSERT/UPDATE

DBTimeStamp($date)

ส่งค่าเวลาในยูนิกซ์ หรือวันที่แบบ ISOฟังก์ชันจะแปลงให้เป็นข้อมูลเวลาในรูปแบบstring เพื่อใช้ในการ INSERT/UPDATE

SQLDate($date, $fmt)

สร้างรูปแบบวันที่โดยให้อยู่ในรูปแบบ $fmtใช้สำหรับชุดคำสั่ง SELECT

OffsetDate($date, $ndays)

สร้าง offset $date โดย $ndays

Concat($s1, $s2,...)

รวมข้อมูลเข้าด้วยกัน หรืออาจจะใช้ไดรเวอร์mssqlpo สำหรับ mssql ซึ่งอนุญาตให้ใช้ | |ได้

IfNull($fld, $replaceNull)

คืนค่า string ฟังก์ชันนี้เทียบเท่ากับ IFNULLใน MySQL หรือ NVL ของออราเคิล

Param($name)

สร้าง bind placeholders โดยใช้ หรือชื่อที่ตั้งไว้ตามความเหมาะสม

$db->sysDate

ค่าที่เก็บฟังก์ชัน SQL ที่คืนค่าวันที่ ณ ปัจจุบัน

$db->sysTimeStamp

ค่าที่เก็บฟังก์ชัน SQL ที่คืนค่าวันและเวลา ณ ปัจจุบัน

$db->concat_operator

ค่าที่เก็บตัวปฏิบัติการรวมข้อมูลเข้าด้วยกัน

$db->length

ค่าที่เก็บชื่อของฟังก์ชัน SQL strlen

$db->uppercase

ค่าที่เก็บชื่อของฟังก์ชัน SQL strtoupper

$db->random

ค่าที่เก็บ SQL ในการสร้างตัวเลขสุ่มระหว่าง0.00 ถึง 1.00

$db->substr

ค่าที่เก็บชื่อฟังก์ชันของ SQL substring

ADOdb ยังมี Oracle oci8 drivers อีกหลายตัว เพื่อใช้ในงานที่แตกต่างกันออกไปดังนี้

ชื่อไดรเวอร์

คำอธิบาย

oci805

สำหรับ Oracle 8.0.5 ซึ่งไดรเวอร์นี้จะมีSelectLimit() ที่ทำงานช้ากว่า

oci8

เป็นไดรเวอร์ที่ให้ประสิทธิภาพสูงสุด ซึ่งเป็นไดรเวอร์ปริยาย ค่าคีย์ที่ได้รับคืนจากชุดข้อมูลใน recordset จะอยู่ในรูปแบบตัวอักษรใหญ่ทั้งหมด

oci8po

เป็นไดรเวอร์แบบ portable ของออราเคิล ช้ากว่า oci8 นิดหน่อย ซึ่งไดรเวอร์นี้จะใช้ ?แทน :bindvar สำหรับค่าตัวแปร bind ซึ่งเป็นมาตรฐานของระบบฐานข้อมูลอื่นๆ และค่าคีย์ที่ได้รับคืนจากชุดข้อมูลจะเป็นตัวอักษรเล็กทั้งหมด เหมือนระบบฐานข้อมูลอื่น

ต่อไปเป็นตัวอย่างการเรียกใช้ oci8po driver

$db = NewADOConnection('oci8po');

$db->Connect($tns, $user, $pwd);

$db->Execute("insert into atable (f1, f2) values (?,?)", array(12, 'abc'));

หมายเหตุค่าตัวแปร bind ใช้เครื่องหมายคำถามแทน

9.      Connecting to Oracle

ก่อนที่เราจะสามารถใช้ ADOdb เราต้องมี Oracle Client ติดตั้งอยู่ก่อนแล้ว และมีการติดตั้ง oci8 extension ด้วย ซึ่งมีมาแบบ pre-compliedสำหรับ windows แต่เราก็ยังจำเป็นต้องกำหนดให้มันทำงานได้ในไฟล์ php.ini สำหรับตัวอย่างการ comply oci8 extension สำหรับ PHP และApache บนยูนิกซ์ สามารถอ่านรายละเอียดได้ที่ oracle.com

การใช้ Persistent Connections

เรามักพบคำถามที่ถามกันบ่อยๆ ก็คือ เราควรจะใช้การติดต่อแบบถาวรไปยังระบบฐานข้อมูลออราเคิลหรือไม่ ด้วยการติดต่อแบบถาวรที่อนุญาตให้ PHP ใช้การติดต่อที่มีอยู่แล้วได้อีก โดยเรียกใช้หลังจากที่เว็บเพจก่อนหน้านี้ได้ถูกเรียกให้ทำงานเสร็จอย่างสมบูรณ์ การติดต่อแบบไม่ถาวรจะปิดการติดต่ออัตโนมัติหลังจากเว็บเพจนั้นถูกเรียกให้ทำงาน การติดต่อแบบถาวรจะเร็วกว่า เพราะว่าเวลาที่ใช้ในการติดต่อใหม่แต่ละครั้งนั้นนานพอสมควร แต่การติดต่อแบบถาวรนี้ก็มีการใช้ทรัพยากรที่มากกว่า ออราเคิลอนุญาตให้เราใช้งานร่วมกัน และนำมาใช้ซ้ำได้อีกในเซิร์ฟเวอร์โพรเซส ซึ่งเรียกว่า Shared Server หรือที่รู้จักกันในนาม MTS

จากการทำ benchmarks ของผู้เขียน ขอแนะนำให้ใช้การติดต่อแบบไม่ถาวร และกำหนดการใช้งานแบบ Shared Server ซึ่งจะให้ประสิทธิภาพสูงสุด ถ้าไม่ได้เลือกใช้ Shared Server จึงควรจะพิจารณาการติดต่อแบบถาวร

ตัวอย่างการติดต่อ

ในกรณีที่มีปัญหาเรื่องการติดต่อไปยังระบบฐานข้อมูลออราเคิล เรามีตัวอย่างการสร้างการติดต่อดังนี้

a.       PHP และ Oracle ถูกติดตั้งอยู่บนเครื่องเดียวกัน ใช้ SID ที่เป็นค่าปริยาย โดยใช้การติดต่อแบบไม่ถาวร

$conn = NewADOConnection('oci8');

$conn->Connect(false, 'scott', 'tiger');

b.       TNS Name ที่ถูกกำหนดไว้ใน tnsnames.ora หรือ ONAMES หรือ HOSTNAMES ตัวอย่างเช่น 'myTNSโดยใช้การติดต่อแบบถาวร

$conn = NewADOConnection('oci8');

$conn->PConnect(false, 'scott', 'tiger', 'myTNS');

หรือ

$conn->PConnect('myTNS', 'scott', 'tiger');

c.       Host Address และ SID

$conn->Connect('192.168.0.1', 'scott', 'tiger', 'SID');

d.       Host Address และ Service Name

$conn->Connect('192.168.0.1', 'scott', 'tiger', 'servicename');

e.       Oracle connection string

$cstr = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$host)

(PORT=$port))(CONNECT_DATA=(SID=$sid)))";

$conn->Connect($cstr, 'scott', 'tiger');

f.         ADOdb data source name (dns)

$dsn = 'oci8://user:pwd@tnsname/?persist';  # persist is optional

$conn = ADONewConnection($dsn);  # no need for Connect/PConnect

$dsn = 'oci8://user:pwd@host/sid';

$conn = ADONewConnection($dsn);

$dsn = 'oci8://user:pwd@/';   # oracle on local machine

$conn = ADONewConnection($dsn);

ถ้าหากใช้ ADOdb data source names ไม่ต้องใช้คำสั่ง Connect(และ PConnect()

10.  Error Checking

ตัวอย่างในบทความนี้ง่ายต่อการทำความเข้าใจ แต่อาจจะดูง่ายไปหน่อย เพราะเราได้ละเรื่องการจัดการข้อผิดพลาดที่เกิดขึ้น คำสั่งExecute(และ Connect() จะให้ค่า false หากเกิดข้อผิดพลาดขึ้น ดังนั้น การเรียกใช้ Connect() และ Excecute() ในความเป็นจริง ควรจะมีลักษณะดังนี้

function InvokeErrorHandler()

{global $db; ## assume global

    MyLogFunction($db->ErrorNo(), $db->ErrorMsg());

}

if (!$db->Connect($tns, $usr, $pwd)) InvokeErrorHandler();

$rs = $db->Execute("select * from emp where empno>:emp order by empno",

                    array('emp' => 7900));

if (!$rs) return InvokeErrorHandler();

while ($arr = $rs->FetchRow()) {

    print_r($arr);

   echo "


";

}

ท่านสามารถดึงข้อความของข้อผิดพลาด และหมายเลขข้อผิดพลาดของคำสั่ง SQL ที่ถูกสั่งให้ทำงานล่าสุดได้จาก ErrorMsg(และErrorNo() นอกจากนี้ ท่านยังสามารถสร้างฟังก์ชันการจัดการข้อผิดพลาดขึ้นมาเองก็ได้ และ ADOdb ก็ยังสนับสนุนการจัดการเกี่ยวกับเงื่อนไขใน PHP5 ด้วยเช่นกัน

การจัดการ Recordsets ขนาดใหญ่

เนื่องจาก oci8 driver ไม่สนับสนุนการนับจำนวนเรคคอร์ดข้อมูลออกมาในคำสั่ง SELECT จึงมีการสร้างฟังก์ชัน RecordCount() ขึ้นมา โดยเมื่อมีการกำหนดตัวแปร global $ADODB_COUNTRECS ให้เป็น true ซึ่งเป็นค่าปริยายอยู่แล้ว เราสร้างฟังก์ชันนี้ขึ้นมาโดยเก็บสำรองข้อมูลทุกเรคคอร์ด ทำให้สามารถใช้หน่วยความจำขนาดใหญ่เพื่อจัดเก็บข้อมูลจำนวนมากได้ แต่อย่างไรก็ตาม การกำหนดค่า$ADODB_COUNTERRECS ให้เป็น false จะทำให้มีประสิทธิภาพที่ดีกว่า

ตัวแปรนี้จะถูกตรวจสอบทุกครั้งที่มีการเรียกดูข้อมูล ดังนั้นท่านสามารถเลือกได้ว่าจะให้ recordset ตัวใดที่จะถูกนับ

11.  Other ADOdb Features

อีกหนึ่ง Feature ของ ADOdb ก็คือ Schema generation ซึ่งช่วยให้ท่านกำหนด schema โดยใช้ XML และนำเข้าไปยังระบบ RDBMS ที่แตกต่างกันออกไปได้

และอีก Feature ก็คือ Performance monitoring and tracing ซึ่งมีคุณสมบัติเด่นก็คือ ความสามารถในการระบุ SQL ที่น่าสงสัย และทำงานช้า โดยมีการอธิบายแนวทางการแก้ไข และระบุ web page ที่ SQL นี้ทำงานอยู่ให้ด้วย

12.  Download

ท่านสามารถดาวน์โหลด ADOdb ได้จาก sourceforge ซึ่ง ADOdb มีลิขสิทธิ์แบบ BSD นั่นก็หมายถึง ท่านสามารถใช้ในการพาณิชย์ได้ และอนุญาตให้จัดจำหน่ายได้โดยไม่ต้องมี source code

13.  Resources

ท่านสามารถหาข้อมูลได้จากแหล่งอื่นๆ ดังนี้

-          Oracle's Hitchhiker Guide to PHP

-          Oracle FAQ on PHP

-          คู่มือ PHP oci8

-          ADOdb Forums

จาก: http://www.exzilla.net/docs/adodb/adodb-oracle-tutorial.php



ขึ้นไปด้านบน