Android의 SQlite에서 준비된 문을 어떻게 사용합니까?


100

Android의 SQlite에서 준비된 문을 어떻게 사용합니까?


9
수락 된 답변을 변경해보십시오.
Suragch 2015-06-29

9
동의했습니다-대답을 바꾸는 것을 고려하십시오 ... 더 나은 해결책이 존재할 때 무리 정신이 받아 들여진 대답을 찬성했습니다.
goodguys_activate jul.

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이 아닌 기반.
Simulant 2013

6
@jasonhudgins SELECT를 INSERT로 바꾸지 않는 이유는 무엇입니까? 나는 방금 스레드 에서 왔는데 , 당신은 초보자를 혼란스럽게했습니다
keyser

35
upvotes와는 완전히 잘못된 답변을 받아들이는 무리 정신의 완벽한 예

165

Android에서 준비된 SQLite 문에는 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()메서드는 값을 반환하지 않으므로 CREATE 및 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()메서드는 DELETE 문에도 사용할 수 있으며 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();

일반적으로 SQLiteDatabasequery() 메서드를 실행하여 커서를 가져옵니다.

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

.query, .insert 및 .delete와 같은 Android 편의 메서드의 내부를 살펴 보았고 내부에서 SQLiteStatement를 사용하는 것으로 나타났습니다. 자신의 진술을 작성하는 대신 편의 방법을 사용하는 것이 더 쉬울까요?
Nicolás Carrasco 2016

@ NicolásCarrasco, 내가 이것에 대해 일한 지 오래되어서 지금은 약간 녹슬 었습니다. 쿼리 및 단일 삽입, 업데이트 및 삭제의 경우 편의 방법을 확실히 사용합니다. 그러나 대량 삽입, 업데이트 또는 삭제를 수행하는 경우 트랜잭션과 함께 준비된 진술을 고려할 것입니다. 내부에서 사용되는 SQLiteStatement와 편의 방법이 충분한 지 여부에 대해서는 말할 수 없습니다. 편의 방법이 충분히 빠르게 수행되고 있다면 사용하십시오.
Suragch

clearBindings ()를 사용하는 이유는 무엇입니까? 조건없이 모든 가치를 묶습니다. 먼저 null로 설정하고 실제 값으로 설정하는 것은 이해가되지 않습니다.
놀라운 1

@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 쿼리이므로 본질적으로 준비된 문입니다.


위 코드의 작은 실수 : "new String {strCountryCode}"대신 "new String [] {strCountryCode}"여야합니다.
Pierre-Luc Simard 2011

데이터를 검색하려면 커서를 이동해야합니다.
Chin은

9

jasonhudgins 예제가 작동하지 않습니다. 로 쿼리를 실행하고 stmt.execute()값 (또는 Cursor)을 다시 가져올 수 없습니다 .

할 수 있습니다 중 하나를 전혀 반환하지 행 또는 단일 행과 열, (사용 (예 : 삽입과 같은, 또는 테이블 문 작성) 만 사전 컴파일 문 simpleQueryForLong()또는 simpleQueryForString()).


1

커서를 얻으려면 compileStatement를 사용할 수 없습니다. 그러나 완전히 준비된 SQL 문을 사용하려면 jbaez의 방법을 적용하는 것이 좋습니다 . db.rawQuery()대신 db.query().

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.