Showing posts with label oracle. Show all posts
Showing posts with label oracle. Show all posts

Friday, April 30, 2010

เซ็ท Parameter ผิด คิดจนหัวบวม

ถ้าเราเซ็ทพารามิเตอร์บางตัวผิด (Oracle ไม่สามารถยอมรับค่านั้นได้) อาจจะทำให้เราไม่สามารถ Startup Database ได้ ดังเช่นตัวอย่างของการตั้งค่าพารามิเตอร์ SGA_MAX_SIZE ซึ่งจะต้อง Restart ระบบฯ หากค่าที่กำหนดเป็นค่าที่ไม่สามารถยอมรับได้ (Invalid) จะทำให้ระบบฯ Startup ไม่ขึ้น ดังเรื่องของสมสรวงที่จะกล่าวถึงต่อไปนี้

สมสรวง ต้องการเพิ่มหน่วยความจำให้กับระบบฐานข้อมูลออราเคิล โดยเขาต้องการให้ฐานข้อมูลใช้หน่วยความจำ 6GB โดยการเซ็ทพารามิเตอร์ SGA_TARGET ซึ่งสามารถทำได้โดยไม่ต้อง Restart ระบบฯ โดย Server มีหน่วยความจำทั้งหมดอยู่ 8GB บนเครื่อง โดยเขาต้องการเซ็ท SGA_MAX_SIZE ให้มากที่สุดเท่าที่จะทำได้ เขาจึงตัดสินใจเซ็ท SGA_MAX_SIZE ให้เท่ากับหน่วยความจำทั้งหมดที่มี

พารามิเตอร์ SGA_MAX_SIZE ใช้ในการกำหนดลิมิตสูงสุดของหน่วยความจำที่สามารถกำหนดให้ SGA_TARGET ได้ เป็นพารามิเตอร์ที่ต้อง Restart ระบบฐานข้อมูลจึงจะมีผล ในขณะที่พารามิเตอร์ SGA_TARGET สามารถกำหนดได้โดยไม่ต้อง Restart ระบบฯ

SQL> alter system set sga_max_size=8g scope=spfile;

System altered.

SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORA-27102: out of memory
OSD-00022: additional error information
O/S-Error: (OS 8) Not enough storage is available to process this command.

เนื่อง จาก SGA_MAX_SIZE เป็นพารามิเตอร์ที่ต้องการการ Restart ระบบฐานข้อมูล แต่เมื่อเขา Startup ฐานข้อมูลปรากฎว่าเขาพบ Error ORA-27102: out of memory และไม่สามารถที่จะเปิดฐานข้อมูลได้เลย

สาเหตุที่เป็นเช่นนี้ เนื่องจาก เมื่อสมสรวงใช้คำสั่ง alter system set sga_max_size=8g scope=spfile นั้น ออราเคิลจะบันทึกค่าของพารามิเตอร์นี้ไว้ในไฟล์ %ORACLE_HOME%\dbs\SPFILEsid.ORA และเมื่อสมสรวงใช้คำสั่ง STARTUP เพื่อเปิดระบบฐานข้อมูล ออราเคิลจะไปอ่านพารามิเตอร์ในไฟล์นี้ในการเปิดระบบ เนื่องจากค่าที่ระบุ SGA_MAX_SIZE มีขนาดเกินกว่าที่หน่วยความจำที่มี (หน่วยความจำที่มีอยู่ 8G นั้นส่วนหนึ่งถูกใช้ไปโดย O/S) จึงไม่สามารถเปิดระบบฐานข้อมูลได้

ผมว่าออราเคิลน่าจะมีฟังก์ชันในการตรวจสอบพารามิเตอร์ใน SPFile ก่อนที่จะยอมปิดระบบฐานข้อมูล และแจ้งเตือนให้ผู้บริหารระบบทราบถึงผล (Error) อันอาจจะเกิดขึ้นหากมีการ Startup ด้วย SPFile ตัวปัจจุบัน

การแก้ไข
ใน %ORACLE_BASE%\admin\orcl10g\pfile\ มีไฟล์พารามิเตอร์ชื่อ init.ora.xxxxxxxxxxxx อยู่ ซึ่งเป็นเท็กซ์ไฟล์ธรรมดา (ต่างจาก SPFile ซึ่งเป็น Binary) เราสามารถเลือกที่จะ Startup ระบบฯ ด้วยไฟล์พารามิเตอร์ตัวนี้แทน SPFile ได้ เราเรียกไฟล์นี้ว่า Pfile (Parameter File) เป็นไฟล์ลูกพี่ลูกน้องกับ Server Parameter File (SPFile) ซึ่งค่าของพารามิเตอร์ที่กำหนดไว้ภายในอาจจะเหมือนหรือต่างกันก็ได้

และ เนื่องจากการที่เราพยายาม Startup ด้วย SPFile ครั้งล่าสุดทำให้ระบบจำค่าพารามิเตอร์ที่ผิดเอาไว้ เราจึงจะต้องเคลียร์ค่าเหล่านี้โดยปิด Service => ลบหรือเปลี่ยนชื่อของ SPFile (ตัวเก่า) => แล้วเปิด Service ใหม่ => แล้วจึง Startup ระบบฯ ด้วย PFile

SQL> host
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\tanakorn>oradim -shutdown -shuttype srvc -sid orcl10g

C:\Documents and Settings\tanakorn>ren C:\oracle\product\10.2.0\db_1\dbs\spfileorcl10g.ora spfileorcl10g_ori.ora

C:\Documents and Settings\tanakorn>oradim -startup -sid orcl10g

C:\Documents and Settings\tanakorn>exit

SQL> conn / as sysdba
Connected to an idle instance.
SQL> startup pfile='C:\oracle\product\10.2.0\admin\orcl10g\pfile\init.ora.312255320268'
ORACLE instance started.

Total System Global Area 612368384 bytes
Fixed Size 1250428 bytes
Variable Size 167775108 bytes
Database Buffers 436207616 bytes
Redo Buffers 7135232 bytes
Database mounted.
Database opened.

หลังจากที่เราเปิดระบบฯ ได้แล้ว เราควรจะสร้าง SPFile จาก Pfile ที่เราใช้ในการเปิดระบบฯ เอาไว้ เนื่องจาก SPFile ตัวเก่าใช้ไม่ได้แล้ว เมื่อสร้างเสร็จแล้วให้ Restart ระบบฯ อีกทีเพื่อให้ระบบฯ กลับมาใช้ SPFile อย่างเดิม

การเปิดฐานข้อมูลด้วย SPFile มีประโยชน์ที่เห็นได้ชัดคือทำให้เราสามารถที่จะเปลี่ยนค่าในพารามิเตอร์ได้ โดยสะดวก โดยไม่ต้องไปแก้ไขในเท็กซ์ไฟล์อย่าง PFile

SQL> create spfile from pfile='C:\oracle\product\10.2.0\admin\orcl10g\pfile\init.ora.312255320268';

File created.

SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area 612368384 bytes
Fixed Size 1250428 bytes
Variable Size 167775108 bytes
Database Buffers 436207616 bytes
Redo Buffers 7135232 bytes
Database mounted.
Database opened.
SQL>

เป็นการเตรียมการที่ดีที่จะสร้าง PFile จาก SPFile ตัวปัจจุบันทุกครั้้ง ก่อนที่จะแก้ไขพารามิเตอร์ที่จะต้องทำการ Restart ระบบฯ เพื่อที่หากเกิดปัญหาจะสามารถนำเอา PFile ตัวนั้นมาใช้ Start ระบบฯได้เลย จากตัวอย่างเราใช้ไฟล์เก่า ซึ่งพารามิเตอร์บางตัวอาจจะไม่เหมือนกับที่ปรากฏใน SPFile ซึ่งอัพเดทกว่าเสมอ

SQL> conn / as sysdba
Connected.
SQL> create pfile from spfile;

File created.

Pfile ที่สร้างขึ้นโดยดีฟอลต์จะไปอยู่ที่ %ORACLE_HOME%\database\ (หรือในกรณีนี้คือ C:\oracle\product\10.2.0\db_1\database\)

Monday, April 5, 2010

อินเด็กซ์ที่ค่าไม่ซ้ำ (Unique Index)

อินเด็กซ์ที่ค่าไม่ซ้ำ (Unique Index) ในที่นี้หมายถึง B-Tree Index ที่เป็นแบบ Unique ซึ่งถือเป็นสุดยอดของอินเด็กซ์เนื่องจากความเร็วและการนำไปใช้งานที่ง่าย เราจะสร้างตารางใหม่ชื่อ MY_OBJECTS จากตาราง ALL_OBJECTS (ALL_OBJECTS เป็นตาราง Meta Data ของ Oracle มีอยู่ในทุก Schema) โดยมีฟิลด์ OBJECT_ID เป็นเลขรันนิ่ง เราจะใช้ฟิลด์นี้ในการสร้างอินเด็กซ์แบบไม่ซ้ำ

SQL> conn scott/tiger
Connected.

SQL> create table tmp_objects as select owner,object_name from all_objects;

Table created.

SQL> insert into tmp_objects select * from tmp_objects;

107518 rows created.

SQL> insert into tmp_objects select * from tmp_objects;

215036 rows created.

SQL> insert into tmp_objects select * from tmp_objects;

430072 rows created.

SQL> insert into tmp_objects select * from tmp_objects;

860144 rows created.

SQL> insert into tmp_objects select * from tmp_objects;

1720288 rows created.

SQL> commit;

Commit complete.

SQL> create table my_objects as select rownum as object_id,owner,object_name from tmp_objects;

Table created.

SQL> select count(*) from my_objects;

COUNT(*)
----------
3440576

SQL> drop table tmp_objects;

Table dropped.

หลังจากนั้นเราสร้างอินเด็กซ์แบบค่าไม่ซ้ำ (Unique Index) บนคอลัมน์ OBJECT_ID เมื่อเราคิวรีโดยใช้ OBJECT_ID=15000 แล้วจะพบว่า Optimizer สามารถใช้วิธีการค้นข้อมูลแบบอินเด็กซ์ค่าเดียว (Index Unique Scan) ซึ่งมีความเร็วสูงที่สุด แต่ถ้าเราใช้ OBJECT_ID > 15000 แล้วทำให้ Optimizer ไม่มีทางเลี่ยงที่จะต้องค้นหาแบบอินเด็กซ์ช่วงแทน (Index Range Scan) สังเกตดูค่าในคอลัมน์ Cost (%CPU) ของการคิวรีทั้งสองแบบ จะเห็นว่าการค้นหาแบบอินเด็กซ์ช่วงใช้เวลาสูงกว่าแบบอินเด็กซ์ค่าเดียว

SQL> create unique index myobjects_idx1 on my_objects(object_id);

Index created.

SQL> select * from my_objects where object_id = 15000;

Execution Plan
----------------------------------------------------------
Plan hash value: 3640416373

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 47 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MY_OBJECTS | 1 | 47 | 3 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | MYOBJECTS_IDX1 | 1 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("OBJECT_ID"=15000)

SQL> select * from my_objects where object_id < 15000;

Execution Plan
----------------------------------------------------------
Plan hash value: 2829829274

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50492 | 2317K| 433 (1)| 00:00:06 |
| 1 | TABLE ACCESS BY INDEX ROWID| MY_OBJECTS | 50492 | 2317K| 433 (1)| 00:00:06 |
|* 2 | INDEX RANGE SCAN | MYOBJECTS_IDX1 | 50492 | | 122 (1)| 00:00:02 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("OBJECT_ID"<15000)

Note
-----
- dynamic sampling used for this statement

คราวนี้ลองสร้างอินเด็กซ์ที่ค่าซ้ำกันได้บ้าง (Non Unique Index) บนคอลัมน์ OBJECT_ID เช่นกัน เมื่อเราคิวรีไม่ว่าจะโดยใช้ OBJECT_ID=15000 หรือ OBJECT_ID>15000 จะพบว่า Optimizer จะไม่สามารถใช้วิธีการค้นข้อมูลแบบอินเด็กซ์ค่าเดียว (Index Unique Scan) ได้เลย เนื่องจากอินเด็กซ์ที่เราสร้างไม่ได้เป็นอินเด็กซ์ที่ค่าไม่ซ้ำ (Unique Index) นั่นเอง ลองสังเกตดูอีกว่าเมื่อเราใช้เงื่อนไข OBJECT_ID>1500000 Optimizer จะใช้วิธีการ TABLE ACCESS FULL แทน เนื่องจากมันรู้ว่ามีข้อมูลจำนวนมากที่มี OBJECT_ID > 1500000 การเข้าไปหาข้อมูลตรง ๆ จากตารางน่าจะเร็วกว่าการไปค้นข้อมูลในอินเด็กซ์ก่อนแล้วค่อยไปหาในตาราง ลองเปรียบเทียบกับการหาหนังสือในห้องสมุด ถ้าเรารู้เลขเรียกหนังสือเราก็จะไปที่ตู้ดัชนีแล้วดูว่าหนังสืออยู่ที่ไหน แต่ถ้าเราต้องการหาหนังสือจำนวนครึ่งหนึ่งของหนังสือทั้งห้องสมุด เราคงไม่ไปใช้ตู้ดัชนี การเดินไปที่หิ้งแล้วไล่ไปทีละเล่มเลยดูเหมือนจะเร็วกว่า

SQL> drop index myobjects_idx1;

Index dropped.

SQL> create index myobjects_idx1 on my_objects(object_id);

Index created.

SQL> select * from my_objects where object_id = 15000;

Execution Plan
----------------------------------------------------------
Plan hash value: 2829829274

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 47 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MY_OBJECTS | 1 | 47 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | MYOBJECTS_IDX1 | 1 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("OBJECT_ID"=15000)

Note
-----
- dynamic sampling used for this statement

SQL> select * from my_objects where object_id < 15000;

Execution Plan
----------------------------------------------------------
Plan hash value: 2829829274

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50492 | 2317K| 441 (1)| 00:00:06 |
| 1 | TABLE ACCESS BY INDEX ROWID| MY_OBJECTS | 50492 | 2317K| 441 (1)| 00:00:06 |
|* 2 | INDEX RANGE SCAN | MYOBJECTS_IDX1 | 50492 | | 130 (1)| 00:00:02 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("OBJECT_ID"<15000)

Note
-----
- dynamic sampling used for this statement

สรุปก็คือ ปกติการค้นข้อมูลแบบอินเด็กซ์ค่าเดียว (Index Unique Scan) จะเร็วกว่าการค้นหาแบบอินเด็กซ์ช่วง (Index Range Scan) ดังนั้นในทุก ๆ กรณีถ้าค่าในคอลัมน์นั้นไม่ซ้ำกันเลยให้ใช้อินเด็กซ์ที่ไม่มีค่าซ้ำ (Unique Index) เพื่อให้ Optimizer สามารถค้นข้อมูลแบบอินเด็กซ์ค่าเดียวไ้ด้เสมอ และในกรณีใด ๆ ถ้าเราสามารถจะใช้เครื่องหมายเท่ากับใน Where Clause ได้ย่อมจะให้ผลดีกว่าการใช้เครื่องหมายมากกว่าหรือน้อยกว่าหรือ LIKE หรืออื่น ๆ เนื่องจากสาเหตุเดียวกัน นอกจากนี้การ Optimizer อาจจะเลือกที่จะทำการตรงไปดูค้นข้อมูลในตารางโดยตรงเลยก็ได้ หากมันเห็นว่าการทำแบบนั้นเร็วกว่า โดยปกติถ้าข้อมูลที่จะดึงออกมามีปริมาณไม่เกิน 10 เปอร์เซ็นต์ของข้อมูลทั้งหมดแล้ว Optimizer จึงจะไปใช้อินเด็กซ์ (อาจจะมากหรือน้อยกว่าขอให้ผู้อ่านลองไปทดสอบเป็นการบ้านดูนะครับ) เช่น ถ้าข้อมูลมี 3 ล้านเรคคอร์ด ถ้าคิวรีโดยใช้ Where Clause แล้วผลที่ได้ไม่เกิน 3 แสนเรคคอร์ด Optimizer จะใช้อินเด็กซ์ทีมีในการคิวรี แต่ถ้าเกินมันก็จะไปค้นข้อมูลจากตารางโดยตรง

Wednesday, June 17, 2009

การใช้ Constraints เพื่อเพิ่มประสิทธิภาพในการคิวรี (ตอนที่ 2)

 การใช้ Constraints เพื่อเพิ่มประสิทธิภาพในการคิวรี (ตอนที่ 2)
(แปลจาก “On Constraints, Metadata, and Truth” โดย Tom Kyte, Oracle Magazine V XXIII, Issue3)

Constraints, Primary Keys and Foreign Keys
คราวนี้ลองมาดู Primary และ Foreign Keys ว่ามีผลต่อ Optimizer อย่างไรบ้าง ตัวอย่างต่อไปนี้ เราจะคัดลอกตารางจาก SCOTT.EMP และตาราง SCOTT.DEPT เราจะสมมติ


ว่าตารางทั้งสองนี้มีขนาด ใหญ่ โดยเราจะใช้ DBMS_STATS.SET_TABLE_STATS เพื่อให้ Optimizer คิดว่า "ใหญ่" และเราสร้างวิว EMP_DEPT ดังใน Listing 7

==========================================================
Listing 7: สร้างตาราง EMP และ DEPT ขนาด "ใหญ่" และวิว EMP_DEPT
SQL> create table emp
2 as
3 select *
4 from scott.emp;

Table created.


SQL> create table dept

2 as
3 select *
4 from scott.dept;

Table created.


SQL> create or replace view emp_dept

2 as
3 select emp.ename, dept.dname
4 from emp, dept
5 where emp.deptno = dept.deptno;

View created.


SQL> begin

2 dbms_stats.set_table_stats
3 ('TANAKORN','EMP',numrows=>1000000, numblks=>100000);
4 dbms_stats.set_table_stats
5 ('TANAKORN','DEPT',numrows=>100000, numblks=>10000);
6 end;
7 /

PL/SQL procedure successfully completed.

==========================================================
คราว นี้ลองสมมติด้วยว่าวิว EMP_DEPT จะถูกใช้ในการคิวรีตาราง EMP และ DEPT และใช้ในการคิวรีผลของการ Join ระหว่างสองตาราง เมื่อเราใช้วิวในการดึงข้อมูลจากตาราง

EMP เราสังเกตว่า ระบบฐานข้อมูลจะ Access ทั้งตาราง EMP และ DEPT ใน Execution Plan ดังแสดงใน Listing 8

==========================================================
Listing 8: คิวรีบนวิว EMP_DEPT จะ Access ตารางทั้ง EMP และ DEPT

SQL> select ename from emp_dept;

Execution Plan

----------------------------------------------------------
Plan hash value: 615168685

-----------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 31M| | 23301 (1)| 00:04:40 |
|* 1 | HASH JOIN | | 1000K| 31M| 2448K| 23301 (1)| 00:04:40 |
| 2 | TABLE ACCESS FULL| DEPT | 100K| 1269K| | 1944 (1)| 00:00:24 |
| 3 | TABLE ACCESS FULL| EMP | 1000K| 19M| | 19444 (1)| 00:03:54 |
-----------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------
1 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")

==========================================================
เนื่องจากเรารู้ว่าถ้าเราต้องการ ENAME การ Access ตาราง DEPT นั้นไม่จำเป็นเลย เพราะว่า DEPTNO เป็น Primary Key ของ DEPT และ DEPTNO ในตาราง EMP ก็เป็น Foreign Key ที่อ้างอิงตาราง DEPT หมายความว่าถ้าเรา Join ตาราง EMP และ DEPT โดยใช้ DEPTNO และทุก ๆ Row ของตาราง EMP ที่ DEPTNO ไม่เป็น Null จะจับคู่กับ 1 เรคคอร์ดในตาราง DEPT เสมอ เรารู้ว่า DEPTNO ใน EMP จะจับคู่ได้ 1 เรคคอร์ดเสมอเพราะ Foeign Key ที่กำหนดไว้บนตาราง EMP และ Primary Key ที่กำหนดไว้ใน DEPT

SQL> alter table dept add constraint dept_pk primary key(deptno);
Table altered.

SQL> alter table emp add constraint emp_fk_dept foreign key(deptno)
2 references dept(deptno);
Table altered.

เราจะได้ Query Plan ดังนี้
SQL> select ename from emp_dept;

Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 126 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 14 | 126 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("EMP"."DEPTNO" IS NOT NULL)

เราจะเห็นว่า Optimizer ได้ตัดเอาตาราง DEPT ออกจากการคิด จะเห็นว่า Hash Join ไม่มีแล้ว และมี Predicate (คำสั่งที่กำหนดถูกกำหนดขึ้นเองโดยระบบ) เพิ่มขึ้นมาว่า DEPTNO IS NOT NULL ซึ่งอธิบายได้ว่า Optimizer รู้ว่าการมี FK และ PK จะทำให้คำสั่ง SELECT ENAME FROM EMP WHERE DEPTNO IS NOT NULL มีผลเท่ากับ คิวรีที่ใช้ในวิว การ Optimize ในลักษณะนี้ (การตัดเอาตารางที่ไม่จำเป็นออกไป) จะทำให้การคิวรีได้ผลเร็วขึ้นมาก โดยเฉพาะถ้าวิวของเรา Join ตารางจำนวนมาก ๆ เอาไว้ แล้ว User จะ select ข้อมูลจากวิวตัวนี้ และไม่ได้ต้องการข้อมูลจากตารางอื่น ๆ ที่อยู่ในวิวตัวนั้นด้วย

ในตัวอย่างยังได้แสดงให้เห็นว่าการ "SELECT * FROM ..." ไม่ควรจะนำมาใช้ในชีวิตจริง เนื่องจากเราจะไม่ได้ประโยชน์จาก Optimization และ Optimizer จะ Access ตาราง DEPT ตลอด เนื่องจากมันคิดว่าเราต้องการข้อมูลจากมัน (* หมายถึงเอาข้อมูลทุกอย่าง) ดังนั้น "ระบุคอลัมน์ที่ต้องการใช้จริงเสมอในคิวรีของคุณ"

(ยังไม่จบ นะครับ ยังมีตอนต่อไป...ขอขอบคุณที่ให้ความสนใจและโปรดติดตามตอนต่อไปนะครับ)

อ่านเพิ่มเติม:
การใช้ Constraints เพื่อเพิ่มประสิทธิภาพในการคิวรี (ตอนที่ 1)
การใช้ Constraints เพื่อเพิ่มประสิทธิภาพในการคิวรี (ตอนที่ 3-ตอนจบ)

Thursday, April 16, 2009

การตรวจสอบการทำงานของ Session

Updated: 5/4/2009

ล็อคอินด้วย User ที่มีสิทธิ์ DBA

1. แสดงการล๊อค ณ ขณะนี้ และการร้องขอการล๊อค
SQL> select * from v$lock;

2. แสดง Process ที่ Active อยู่ ณ ขณะนี้
SQL> select * from v$process;

3.แสดงข้อมูลของ Sessions ที่มีอยู่ ณ ปัจจุบัน เราสามารถเชื่อมโยง SID ไปยัง Sessions อื่น ๆ ได้ นอกจากนี้ยังแสดงข้อมูลของการล๊อคเรคคอร์ดด้วย
SQL> select * from v$session;

4.แสดงข้อมูลของการรอ (เพื่อทำงานอย่างใดอย่างหนึ่ง) ของ Session หนึ่ง ๆ
SQL> select * from v$session_event;

5. แสดงทรัพยากร หรือการทำงานใด ๆ ที่ Session ที่ Active กำลังรอคอย WAIT_TIME = 0 หมายถึง งานปัจจุบันที่ Session ทำอยู่
SQL> select * from v$session_wait;
โครงสร้างของ V$SESSION_WAIT ช่วยให้ง่ายในการเช็คว่า ณ ขณะนั้นมี Session ใดรออยู่บ้าง รวมทั้งสาเหตุด้วย ข้อมูลที่ได้ทำให้เราสามารถตรวจสอบได้ว่า การรอแบบนี้เกิดขึ้นบ่อยหรือเปล่า หรือมีความสัมพันธ์กับเหตุการณ์อื่น ๆ หรือเมื่อมีการเรียกไปยัง Module ใด ๆ หรือเปล่า

6. แสดงข้อมูลทางสถิติของ Session ของ User จะต้องไป Join กับ V$STATNAME และ V$SESSION
SQL> select * from v$sesstat;
ตัวอย่าง
SQL> select a.*,b.name,c.username,c.machine from v$sesstat a, v$statname b, v$session c
where a.sid=c.sid and a.statistic#=b.statistic#
and username='TANAKORNT'
order by a.sid ,a.statistic#;