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.