02 February 2013

SQLite in Android - การใช้ Cursor กับฐานข้อมูล SQLite

Updated on

บทความแรกของเดือนนี้ ก็ยังเป็นพื้นฐานเหมือนเดิมตามเคย
และก็ยังคงอยู่ที่เรื่องฐานข้อมูลอีกเช่นเคย แต่คราวนี้เป็น Cursor
ซึ่งเจ้าของบล็อกเห็นว่า ส่วนมากมีแต่วิธีการใช้ฐานข้อมูล
การดึงฐานข้อมูล การเพิ่มฐานข้อมูล การลบฐานข้อมูล
ทั้งๆที่ Cursor ถูกเรียกใช้อยู่บ่อยๆ แต่มีคนเข้าใจมันน้อยเหลือเกิน
บทความรอบนี้ก็ขอกลับมาสู่เรื่องพื้นฐานสามัญกันหน่อย

สำหรับ Cursor ก็ตามชื่อของมันนั่นแหละ ไม่รู้จะนิยามยังไง
มีหน้าที่เอาไว้ระบุข้อมูลที่เลือก เวลาที่มีข้อมูลหลายๆตัว

นึกง่ายๆก็ มีปุ่มอยู่สี่ปุ่ม แล้วต้องการเลือกปุ่มใดปุ่มหนึ่ง
ผู้ที่หลงเข้ามาอ่านก็ต้องใช้ Cursor เพื่อเลือกปุ่มที่ต้องการ
นึกถึง Cursor เกมก็ได้ ที่เอาไว้เลือกตัวเลือกต่างๆ
ต้องกดปุ่มขึ้น-ลง เพื่อเลื่อนตำแหน่งของ Cursor 


จากภาพข้างบนนี้ก็จะเห็นว่า Cursor ของภาพข้างบนจะเป็นแถบสีฟ้าๆ
โดยมีข้อมูลให้เลือกคือ Artist, Albums, Songs, Genres, .... บลาๆ
และ Cursor กำลังเลือกข้อมูลที่ชื่อว่า Albums อยู่ เข้าใจใช่มั้ย?



ลองดูอีกภาพละกัน (อันนี้เกมโปรดเจ้าของบล็อก เมื่อวัยเยาว์)
จะเห็นว่าเคอร์เซอร์เป็นรูปหน้าลูอี้ (หน้าจิงโจ้สีเหลือง)
โดยมีข้อมูลสามตัวคือ Normal Game, Battle Game และ Options
ซึ่ง Cursor กำลังเลือกข้อมูลที่เป็น Normal Game อยู่


สำหรับ Cursor ในฐานข้อมูล SQLite ก็เหมือนกันนั่นแหละ
เพียงแต่ว่าไม่มีภาพแสดงให้เห็น เพราะมันเป็นฐานข้อมูล
ดังนั้นก็ต้องใช้จินตนาการกันเอาเองว่ามันเป็นยังไง

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


โดย Cursor ทำหน้าที่เลือกข้อมูล ว่าจะเลือกข้อมูลแถวไหน
โดยที่แถวแรกสุดนับจาก 0, 1, 2, 3, ..... (ไม่ใช่่ _id นะ คนละอัน)


ทีนี้ขอข้ามเรื่อง rawQuery ไปนะ เพราะพูดไปในบทความเก่าแล้ว
การใช้คำสั่งต่างๆของ Cursor ก็ควรหลังจากใช้คำสั่ง rawQuery
ทีนี้ก็มาดูกันว่า Cursor มีคำสั่งอะไรบ้าง เอาแค่หลักๆพอนะ

สมมติว่าคำสั่ง rawQuery เจ้าของบล็อกเลือกข้อมูลทุกตัวที่มี
mCursor = mDb.rawQuery("SELECT * FROM " + Database.TABLE_NAME, null);

เมื่อ mCursor ถูกกำหนดค่าด้วยคำสั่ง mDb.rawQuery
mCursor จะยังไม่ได้เลือกแถวใดๆนะ แต่จะเป็น -1
ซึ่งก็คือยังไม่มีการเลือกข้อมูลแถวไหนเลย
ถ้าไปดึงข้อมูลตอนนี้ละก็เออเรอแน่ (พลาดกันเยอะ)

แต่มาดูคำสั่ง mCursor.getCount(); กันก่อนดีกว่า
เป็นคำสั่งเช็คว่ามีข้อมูลที่ดึงมาเก็บใน mCursor อยู่กี่ตัว
ดังนั้นถ้าอยากรู้ว่ามีข้อมูลดึงมาเก็บใน mCursor มั้ย
ก็ให้ใช้คำสั่งนี้แล้วแสดงค่าออกมาผ่าน LogCat ดู
Log.i("Data Count", String.valueOf(mCursor.getCount));

ทีนี้มาดูวิธีการเลื่อนตำแหน่ง Cursor กันต่อเลย
สำหรับคำสั่งการเลื่อนตำแหน่งนั้นไม่ยากเลย เริ่มจาก
mCursor.moveToFirst();
ความหมายตรงตัวเลย คือเลื่อน Cursor ไปแถวแรกสุด
ดังนั้นจากฐานข้อมูลที่ใช้อ้างอิง Cursor ก็จะเลือกข้อมูลแถวแรก




mCursor.moveToLast();
คำสั่งนี้ก็คือเลื่อนตำแหน่ง Cursor ไปที่แถวสุดท้าย
ดังนั้น Cursor ก็จะเลือกข้อมูลแถวสุดท้ายเลย




mCursor.moveToPosition(position);
สำหรับคำสั่งนี้ก็คือการเลื่อน Cursor ไปแถวที่ต้องการ
โดยที่ position ก็คือตัวแปร int ที่กำหนดแถวที่ต้องการ
สมมติว่าใส่ตัวแปรเป็น 3 เข้าไปแทนที่ position ก็จะได้
(อย่าลืมนะว่านับแถวเริ่มจาก 0, 1, 2, 3, 4, ... , X)
แล้วก็อย่าเผลอไปใส่เลขเกินจำนวนข้อมูลที่มีล่ะ




mCursor.moveToNext();
สำหรับคำสั่งนี้ก็คือการเลื่อน Cursor ไปยังแถวถัดไป
คำสั่งนี้ก็ให้ระวังเรื่องเลื่อนจนเกินจำนวนข้อมูลที่มีเช่นกัน





mCursor.moveToPrevious();
สำหรับคำสั่งนี้ก็คือการเลื่อน Cursor ไปแถวก่อนหน้า
อย่าเผลอเลื่อน Cursor จนถึงแถว -1 ล่ะ เดี๋ยวดึงข้อมูลแล้วเออเรอ





mCursor.move(offset);
สำหรับคำสั่งนี้คือการเลื่อน Cursor โดยอิงจากตำแหน่งเดิม
ว่าจะให้เลื่อนจากตำแหน่งเดิมไปกี่แถว โดยกำหนดที่ offset





หมดแหละ สำหรับคำสั่งเลื่อนตำแหน่ง Cursor
คราวนี้ก็มาถึงคำสั่งเช็คตำแหน่ง Cursor กันบ้าง
โดยใช้คำสั่ง mCursor.getPosition(); ค่าที่ได้จะเป็น int
Log.i("Cursor Position", String.valueOf(mCursor.getPosition()));

และสามารถเช็คว่าอยู่บรรทัดแรกสุดหรือป่าว ได้เช่นกัน
โดยใช้คำสั่ง mCursor.isFirst(); ค่าที่ได้จะเป็น Boolean
Log.i("Is First", String.valueOf(mCursor.isFirst()));

คำสั่ง mCursor.isBeforeFirst(); เช็คว่าตำแหน่ง Cursor เป็น -1 หรือไม่
ซึ่งก็คือ Cursor ยังไม่ได้เลือกแถวใด โดยค่าที่ได้จะเป็น Boolean
Log.i("Is Before First", String.valueOf(mCursor.isBeforeFirst()));

คำสั่ง mCursor.isLast(); เพื่อเช็คว่า Cursor อยู่แถวสุดท้ายหรือไม่
โดยขึ้นอยู่กับจำนวนข้อมูลทั้งหมดด้วย ค่าที่ได้ก็จะเป็น Boolean
Log.i("Is Last", String.valueOf(mCursor.isLast()));

คำสั่ง mCursor.isAfterLast(); เพื่อเช้คว่า Cursor เลยแถวสุดท้ายหรือไม่
เอาไว้ป้องกันเลื่อน Cursor เลยแถวสุดท้ายนั่นเอง ค่าที่ได้เป็น Boolean
Log.i("Is After Last", String.valueOf(mCursor.isAfterLast()));


ต่อมาก็เป็นเรื่องของคอลัมน์ (Column) กันต่อเลย
ก่อนอื่นก็เริ่มจากรับค่าจำนวนคอลัมน์ของฐานข้อมูลก่อน
โดยใช้คำสั่ง mCursor.getColumnCount();
Log.i("Is After Last", String.valueOf(mCursor.getColumnCount()));

สำหรับคอลัมน์จะมีการใช้ตัวแปรสองแบบคือ
Column Name (String) กับ Column Index (Integer)
ก่อนอื่นขอแก้ไขฐานข้อมูลกันก่อน เพราะว่าไม่มี _id
ซึ่งจริงๆแล้วจะต้องมี แต่เจ้าของบล็อกเอาออกไป
เพราะว่าเวลาอธิบายเรื่องแถวของ Cursor เริ่มจาก 0
แต่ _id เริ่มจาก 1 ก็เลยเอาออกไปก่อน จะได้ไม่งง
แต่คราวนี้จะขออิงข้อมูลจริงๆล่ะ โดยข้อมูลเป็นดังนี้


ทีนี้ก็จะเห็นว่าฐานข้อมูลของเจ้าของบล็อกมีทั้งหมด 4 คอลัมน์
เมื่อใช้คำสั่ง getColumnCount ก็จะได้ค่าเป็นตัวแปร int เท่ากับ 4
สำหรับคอลัมน์ก็จะเริ่มนับจาก 0, 1, 2, 3, ... , X เหมือนกับแถว
ดังนั้น _id คือ 0, name คือ 1, pieceprice คือ 2 และ cakeprice คือ 3

ก่อนอื่นลองดูคำสั่ง mCursor.getColumnIndex(columnName); ก่อน
คำสั่งนี้คือคำสั่งเช็ค Column Index โดยอิงจากชื่อคอลัมน์ที่ใส่
int columnIndex = mCursor.getColumnIndex("pieceprice");
จากคำสั่งดังกล่าว ตัวแปร columnIndex ก็จะมีค่าเท่ากับ 2


ทีนี้มาดูคำสั่ง mCursor.getColumnName(columnIndex); ต่อเลย
สำหรับคำสั่งนี้ก็จะเป็นคำสั่งที่ตรงข้ามกับคำสั่งเมื่อกี้เลย
โดยจะเช็ค Column Name โดยอิงจากลำดับของคอลัมน์ที่ใส่
String columnName = mCursor.getColumnName(1);
จากคำสั่งดังกล่าว ตัวแปร columnName ก็จะมีค่าเป็น "name"

และนอกจากนี้คำสั่งนี้ยังสามารถรับชื่อคอลัมน์เป็น String[] ได้เลย
String[] columnName = mCursor.getColumnNames();
ดูคำสั่งให้ดีนะ อันนี้ลงท้ายด้วยตัว s ก่อนหน้านี้ไม่มี s
โดยที่ columnName จะเป็น String[] เก็บชื่อคอลัมน์ทั้งหมดไว้



คราวนี้มาเข้าหัวใจสำคัญของ Cursor กันเลยดีกว่า
นั่นก็คือการดึงข้อมูลออกมานั่นเอง (เกริ่นซะนานกว่าจะถึง)
สำหรับคำสั่งดึงข้อมูลจาก Cursor ออกมาก็จะมีทั้งหมดดังนี้
String data = mCursor.getString(columnIndex); byte[] data = mCursor.getBlob(columnIndex); double data = mCursor.getDouble(columnIndex); float data = mCursor.getFloat(columnIndex); int data = mCursor.getInt(columnIndex); long data =mCursor.getLong(columnIndex); short data = mCursor.getShort(columnIndex);
ดูแค่นี้ก็น่าจะเข้าใจแล้วล่ะ คงไม่ต้องอธิบายอะไรมาก
และอีกอย่าง ตัวแปรที่จะดึงไม่จำเป็นต้องตรงกับที่เก็บในฐานข้อมูลก็ได้
อย่างเช่นเก็บข้อมูลไว้ในฐานข้อมูลเป็น Integer แต่ก็ดึงเป็น String ได้
แต่ก็อย่าลืมนะว่ากำหนดในฐานข้อมูลเป็น Text แล้วจะดึงมาเป็น Integer
ข้อมูลที่เก็บไว้ในนั้นก็จะต้องเป็นตัวเลขเท่านั้น ห้ามมีตัวอักษรใดๆปน

ให้สังเกตว่าคำสั่งข้างบนนี้จะต้องกำหนด Column Index ที่จะดึงด้วย
ก็เอาคำสั่ง mCursor.getColumnIndex(columnName); มาใช้ร่วมกัน
int columnIndex = mCursor.getColumnIndex("cakeprice"); float data = mCursor.getFloat(columnIndex);

int columnIndex = mCursor.getColumnIndex("cakeprice"); String data = mCursor.getString(columnIndex);
ดูแค่นี้ก็น่าจะเข้าใจแล้วล่ะ คงไม่ต้องอธิบายอะไรมาก

ทีนี้จะเห็นว่าคำสั่งข้างบนนี้สามารถรวบรัดคำสั่งได้เลย
จากคำสั่งที่ใช้สองบรรทัดก็เหลือบรรทัดเดียว ประหยัดตัวแปรด้วย
String data = mCursor.getString(mCursor.getColumnIndex("cakeprice"));



ขั้นตอนการดึงข้อมูลจากฐานข้อมูล

1. เปิดใช้งานฐานข้อมูล
Database mHelper = new Database(this); SQLiteDatabase mDb = mHelper.getWritableDatabase();

2. Query ข้อมูล
Cursor mCursor = mDb.rawQuery("SELECT * FROM my_table", null);

3. เลื่อนตำแหน่ง Cursor ไปยังแถวที่ต้องการ
mCursor.movetToPosition(3);

4. เลือกคอลัมน์ที่ต้องการดึงข้อมูล
String data = mCursor.getString(mCursor.getColumnIndex("name"));

5. เอาค่าที่ได้ไปใช้งานตามใจชอบ
Log.i("GetData", data);

จากตัวอย่างคำสั่งเจ้าของบล็อกเลื่อ Cursor ไปยังแถวที่ 3 (เริ่มนับจาก 0)


ต่อมาเจ้าของบล็อกเลือกดึงข้อมูลเป็น String จากคอลัมน์ "name" 
ดังนั้นข้อมูลที่ได้ก็คือ Cheese Cake แล้วก็เก็บไว้ในตัวแปร data


สำหรับการแสดงข้อมูลทุกแถว ก็ใช้ While หรือ For ในการวนลูปเอา
โดยกำหนดให้ moveToFirst ก่อน พอวนลูปแต่ละรอบก็ให้ moveToNext

ก็จบแล้วล่ะกับบทความในเรื่องของ Cursor ที่ไม่คิดว่าจะพิมเยอะขนาดนี้
เจ้าของบล็อกแนะนำให้ผู้ที่หลงเข้ามาอ่านหัดหรือฝึกเขียนเองดู 
เพื่อให้ใช้จนคล่อง เมื่อเขียนแอพดึงข้อมูลจากฐานข้อมูลก็จิ๊บๆแล้ว

สำหรับผู้ที่หลงเข้ามาอ่านคนไหนต้องการไฟล์ตัวอย่าง
สามารถดาวน์โหลดได้ที่ Database Cursor [Google Drive]


กว่าจะเสร็จก็เช้าแล้วแฮะ = =