ฉันจะใช้คำสั่งที่เตรียมไว้ใน SQlite ใน Android ได้อย่างไร


100

ฉันจะใช้คำสั่งที่เตรียมไว้ใน SQlite ใน Android ได้อย่างไร


9
พิจารณาเปลี่ยนคำตอบที่ยอมรับ
Suragch

9
เห็นด้วย - พิจารณาเปลี่ยนคำตอบ ... ความคิดของฝูงเพิ่มคะแนนคำตอบที่ยอมรับเมื่อมีทางออกที่ดีกว่า
goodguys_activate

4
Pablo โปรดเปลี่ยนคำตอบที่ยอมรับ ... ตัวอย่างที่ระบุไม่ได้ทำงาน และการโหวตดาวน์ของเราไม่เพียงพอที่จะเลิกตรึง
SparK

คำตอบ:


21

ฉันใช้งบที่เตรียมไว้ใน Android ตลอดเวลามันค่อนข้างง่าย:

SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();

81
ฉันไม่รู้ว่าคำตอบนี้มีคะแนนเสียงมากมายได้อย่างไร ไม่ควรใช้ SQLIteStatement # execute สำหรับการสืบค้น Sql เฉพาะคำสั่ง โปรดตรวจสอบdeveloper.android.com/reference/android/database/sqlite/…
simao

9
แล้วคุณจะใช้คำสั่งที่เตรียมไว้สำหรับการสืบค้นข้อมูลได้อย่างไร?
Juan Mendes

12
โปรดทราบว่าSQLiteStatement.bindXXX()มีดัชนี 1 ฐานไม่ใช่ 0 เหมือนดัชนีส่วนใหญ่
จำลอง

6
@jasonhudgins ทำไมไม่แทนที่ SELECT ของคุณด้วย INSERT? ฉันเพิ่งมาจากกระทู้นี้ซึ่งคุณเคยสับสนกับมือใหม่
กุญแจ

35
ตัวอย่างที่สมบูรณ์แบบของความคิดของฝูงที่โหวตและยอมรับคำตอบที่ไม่ถูกต้องทั้งหมด

165

สำหรับงบ SQLite เตรียมใน Android มีSQLiteStatement ข้อความที่เตรียมไว้ช่วยให้คุณเพิ่มความเร็วในการทำงาน (โดยเฉพาะอย่างยิ่งสำหรับคำสั่งที่ต้องดำเนินการหลายครั้ง) และยังช่วยหลีกเลี่ยงการโจมตีด้วยการฉีด ดูบทความนี้สำหรับการอภิปรายทั่วไปเกี่ยวกับแถลงการณ์ที่เตรียมไว้

SQLiteStatementมีขึ้นเพื่อใช้กับคำสั่ง SQL ที่ไม่คืนค่าหลายค่า (นั่นหมายความว่าคุณจะไม่ใช้สำหรับการสืบค้นส่วนใหญ่) ด้านล่างนี้คือตัวอย่างบางส่วน:

สร้างตาราง

String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();

execute()วิธีการไม่คืนค่าจึงมีความเหมาะสมที่จะใช้กับการสร้างและ DROP แต่ไม่ได้มีเจตนาที่จะใช้กับ SELECT, INSERT, DELETE และ UPDATE เพราะเหล่านี้ค่าที่ส่งคืน (แต่ดูคำถามนี้)

แทรกค่า

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();

โปรดทราบว่าวิธีการที่ใช้มากกว่าexecuteInsert() execute()แน่นอนว่าคุณคงไม่ต้องการป้อนสิ่งเดียวกันในทุกแถว สำหรับสิ่งที่คุณสามารถใช้ผูก

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);

int intValue = 57;
String stringValue = "hello";

statement.bindLong(1, intValue); // 1-based: matches first '?' in sql string
statement.bindString(2, stringValue);  // matches second '?' in sql string

long rowId = statement.executeInsert();

โดยปกติคุณจะใช้คำสั่งที่เตรียมไว้เมื่อคุณต้องการทำบางสิ่งซ้ำ ๆ อย่างรวดเร็ว (เช่น INSERT) หลาย ๆ ครั้ง คำสั่งที่เตรียมไว้ทำให้คำสั่ง SQL ไม่จำเป็นต้องแยกวิเคราะห์และคอมไพล์ทุกครั้ง คุณสามารถเพิ่มความเร็วสิ่งขึ้นมากยิ่งขึ้นโดยใช้การทำธุรกรรม สิ่งนี้ช่วยให้สามารถใช้การเปลี่ยนแปลงทั้งหมดได้ในครั้งเดียว นี่คือตัวอย่าง:

String stringValue = "hello";
try {

    db.beginTransaction();
    String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
    SQLiteStatement statement = db.compileStatement(sql);

    for (int i = 0; i < 1000; i++) {
        statement.clearBindings();
        statement.bindLong(1, i);
        statement.bindString(2, stringValue + i);
        statement.executeInsert();
    }

    db.setTransactionSuccessful(); // This commits the transaction if there were no exceptions

} catch (Exception e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

ตรวจสอบลิงก์เหล่านี้เพื่อดูข้อมูลดีๆเพิ่มเติมเกี่ยวกับธุรกรรมและการเร่งการแทรกฐานข้อมูล

อัปเดตแถว

นี่คือตัวอย่างพื้นฐาน คุณยังสามารถใช้แนวคิดจากส่วนด้านบน

String sql = "UPDATE table_name SET column_2=? WHERE column_1=?";
SQLiteStatement statement = db.compileStatement(sql);

int id = 7;
String stringValue = "hi there";

statement.bindString(1, stringValue);
statement.bindLong(2, id);

int numberOfRowsAffected = statement.executeUpdateDelete();

ลบแถว

executeUpdateDelete()วิธีนอกจากนี้ยังสามารถนำมาใช้สำหรับงบลบและเป็นที่รู้จักใน API 11. ดู Q & A นี้

นี่คือตัวอย่าง

try {

    db.beginTransaction();
    String sql = "DELETE FROM " + table_name +
            " WHERE " + column_1 + " = ?";
    SQLiteStatement statement = db.compileStatement(sql);

    for (Long id : words) {
        statement.clearBindings();
        statement.bindLong(1, id);
        statement.executeUpdateDelete();
    }

    db.setTransactionSuccessful();

} catch (SQLException e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

แบบสอบถาม

โดยปกติเมื่อคุณเรียกใช้แบบสอบถามคุณต้องการให้เคอร์เซอร์กลับมาพร้อมกับแถวจำนวนมาก นั่นไม่ใช่สิ่งที่SQLiteStatementมีไว้สำหรับแม้ว่า คุณไม่ต้องเรียกใช้แบบสอบถามด้วยเว้นแต่คุณต้องการเพียงผลลัพธ์ง่ายๆเช่นจำนวนแถวในฐานข้อมูลซึ่งคุณสามารถทำได้ด้วยsimpleQueryForLong()

String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();

โดยปกติคุณจะเรียกใช้query()เมธอดของSQLiteDatabaseเพื่อรับเคอร์เซอร์

SQLiteDatabase db = dbHelper.getReadableDatabase();
String table = "table_name";
String[] columnsToReturn = { "column_1", "column_2" };
String selection = "column_1 =?";
String[] selectionArgs = { someValue }; // matched to "?" in selection
Cursor dbCursor = db.query(table, columnsToReturn, selection, selectionArgs, null, null, null);

ดูคำตอบนี้เพื่อดูรายละเอียดที่ดีขึ้นเกี่ยวกับข้อความค้นหา


5
ขอเตือน: วิธีการ. bindString / .bindLong / ... ทั้งหมดเป็นแบบ 1
Denys Vitali

ฉันกำลังมองหาวิธีอำนวยความสะดวกของ Android เช่น. เคียวรี. แทรกและ. ลบและสังเกตเห็นว่าพวกเขาใช้ SQLiteStatement ภายใต้ประทุน จะง่ายกว่าไหมถ้าใช้วิธีอำนวยความสะดวกแทนการสร้างงบของคุณเอง
Nicolás Carrasco

@ NicolásCarrascoมันเป็นเวลานานแล้วที่ฉันได้ทำงานนี้ดังนั้นตอนนี้ฉันก็รู้สึกเป็นสนิมนิดหน่อย สำหรับคำถามและการแทรกการอัปเดตและการลบเพียงครั้งเดียวฉันจะใช้วิธีอำนวยความสะดวกอย่างแน่นอน อย่างไรก็ตามหากคุณกำลังทำการแทรกอัปเดตหรือลบจำนวนมากฉันจะพิจารณาคำสั่งที่เตรียมไว้พร้อมกับธุรกรรม สำหรับ SQLiteStatement ที่ถูกใช้ภายใต้ประทุนและวิธีการอำนวยความสะดวกนั้นดีพอหรือไม่ฉันไม่สามารถพูดได้ ฉันเดาว่าฉันจะบอกว่าถ้าวิธีการอำนวยความสะดวกทำงานได้เร็วพอสำหรับคุณให้ใช้วิธีนี้
Suragch

ทำไมคุณถึงใช้ clearBindings ()? คุณผูกค่าทั้งหมดของคุณโดยไม่มีเงื่อนไขใด ๆ มันไม่สมเหตุสมผลสำหรับฉันที่จะกำหนดให้เป็นโมฆะก่อนและเป็นค่าจริง
เหลือเชื่อ

@TheincredibleJan ไม่รู้แน่ชัด อาจไม่จำเป็นและคุณสามารถลองได้โดยไม่ต้องล้างการผูกเพื่อดูว่าสร้างความแตกต่างหรือไม่ อย่างไรก็ตามที่กล่าวว่าการโทรclearBindings()ไม่เพียง แต่ตั้งค่าเป็นnull(ดูซอร์สโค้ด ) ฉันมองว่ามันเป็นการล้างสถานะเพื่อไม่ให้ไม่มีอะไรมากระทบกับมันจากลูปก่อนหน้านี้ บางทีนั่นอาจไม่จำเป็น ฉันยินดีสำหรับคนที่รู้จักแสดงความคิดเห็น
Suragch

22

หากคุณต้องการให้เคอร์เซอร์กลับมาคุณอาจพิจารณาสิ่งนี้:

SQLiteDatabase db = dbHelper.getWritableDatabase();

public Cursor fetchByCountryCode(String strCountryCode)
{
    /**
     * SELECT * FROM Country
     *      WHERE code = US
     */
    return cursor = db.query(true, 
        "Country",                        /**< Table name. */
        null,                             /**< All the fields that you want the 
                                                cursor to contain; null means all.*/
        "code=?",                         /**< WHERE statement without the WHERE clause. */
        new String[] { strCountryCode },    /**< Selection arguments. */
        null, null, null, null);
}

/** Fill a cursor with the results. */
Cursor c = fetchByCountryCode("US");

/** Retrieve data from the fields. */
String strCountryCode = c.getString(cursor.getColumnIndex("code"));

/** Assuming that you have a field/column with the name "country_name" */
String strCountryName = c.getString(cursor.getColumnIndex("country_name"));

ดูตัวอย่างGenscriptsนี้ในกรณีที่คุณต้องการให้สมบูรณ์ยิ่งขึ้น โปรดทราบว่านี่เป็นแบบสอบถาม SQL ที่กำหนดพารามิเตอร์ดังนั้นโดยพื้นฐานแล้วมันเป็นคำสั่งที่เตรียมไว้


ข้อผิดพลาดเล็กน้อยในโค้ดด้านบน: ควรเป็น "สตริงใหม่ [] {strCountryCode} แทนที่จะเป็น" สตริงใหม่ {strCountryCode} "
Pierre-Luc Simard

คุณต้องเลื่อนเคอร์เซอร์ก่อนจึงจะดึงข้อมูลได้
ชิน

9

ตัวอย่าง jasonhudgins จะไม่ทำงาน คุณไม่สามารถเรียกใช้แบบสอบถามstmt.execute()และรับค่า (หรือ a Cursor) กลับมาได้

คุณสามารถคอมไพล์ได้เฉพาะคำสั่ง precompile ที่ไม่ส่งคืนแถวเลย (เช่นคำสั่งแทรกหรือสร้างตาราง) หรือแถวและคอลัมน์เดียว (และใช้simpleQueryForLong()หรือsimpleQueryForString())


1

หากต้องการรับเคอร์เซอร์คุณไม่สามารถใช้ compileStatement อย่างไรก็ตามหากคุณต้องการใช้คำสั่ง SQL ที่เตรียมไว้เต็มรูปแบบฉันขอแนะนำให้ปรับวิธีการของ jbaez ... โดยใช้db.rawQuery()แทนdb.query().

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