webentwicklung-frage-antwort-db.com.de

IN-Klausel und Platzhalter

Ich versuche, die folgende SQL-Abfrage in Android auszuführen:

    String names = "'name1', 'name2";   // in the code this is dynamically generated

    String query = "SELECT * FROM table WHERE name IN (?)";
    Cursor cursor = mDb.rawQuery(query, new String[]{names});

Android ersetzt das Fragezeichen jedoch nicht durch die richtigen Werte. Ich könnte Folgendes tun, dies schützt jedoch nicht vor der SQL-Injection:

    String query = "SELECT * FROM table WHERE name IN (" + names + ")";
    Cursor cursor = mDb.rawQuery(query, null);

Wie kann ich dieses Problem umgehen und die IN-Klausel verwenden?

97
Nick

Eine Zeichenfolge des Formulars "?, ?, ..., ?" kann eine dynamisch erstellte Zeichenfolge sein und sicher in die ursprüngliche SQL-Abfrage eingefügt werden (da es sich um ein eingeschränktes Formular handelt, das keine externen Daten enthält) und die Platzhalter dann normal verwendet werden können.

Betrachten Sie eine Funktion String makePlaceholders(int len), die len-Fragezeichen mit Kommas getrennt zurückgibt, und dann:

String[] names = { "name1", "name2" }; // do whatever is needed first
String query = "SELECT * FROM table"
    + " WHERE name IN (" + makePlaceholders(names.length) + ")";
Cursor cursor = mDb.rawQuery(query, names);

Stellen Sie sicher, dass Sie genau so viele Werte wie Orte übergeben. Das Standardlimit Limit der Host-Parameter in SQLite beträgt 999 - zumindest in einem normalen Build, nicht sicher über Android :)

Glückliche Kodierung.


Hier ist eine Implementierung:

String makePlaceholders(int len) {
    if (len < 1) {
        // It will lead to an invalid query anyway ..
        throw new RuntimeException("No placeholders");
    } else {
        StringBuilder sb = new StringBuilder(len * 2 - 1);
        sb.append("?");
        for (int i = 1; i < len; i++) {
            sb.append(",?");
        }
        return sb.toString();
    }
}
182
user166390

Kurzes Beispiel, basierend auf Antwort von Benutzer166390:

public Cursor selectRowsByCodes(String[] codes) {
    try {
        SQLiteDatabase db = getReadableDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

        String[] sqlSelect = {COLUMN_NAME_ID, COLUMN_NAME_CODE, COLUMN_NAME_NAME, COLUMN_NAME_PURPOSE, COLUMN_NAME_STATUS};
        String sqlTables = "Enumbers";

        qb.setTables(sqlTables);

        Cursor c = qb.query(db, sqlSelect, COLUMN_NAME_CODE+" IN (" +
                        TextUtils.join(",", Collections.nCopies(codes.length, "?")) +
                        ")", codes,
                null, null, null); 
        c.moveToFirst();
        return c;
    } catch (Exception e) {
        Log.e(this.getClass().getCanonicalName(), e.getMessage() + e.getStackTrace().toString());
    }
    return null;
}
8
Yuliia Ashomok

Wie in akzeptierter Antwort, aber ohne Verwendung einer benutzerdefinierten Funktion zum Generieren durch Kommas getrenntes "?". Bitte überprüfen Sie den Code unten.

String[] names = { "name1", "name2" }; // do whatever is needed first
String query = "SELECT * FROM table"
    + " WHERE name IN (" + TextUtils.join(",", Collections.nCopies(names.length, "?"))  + ")";
Cursor cursor = mDb.rawQuery(query, names);
5
Kalpesh Gohel

Leider gibt es keine Möglichkeit, dies zu tun (offensichtlich ist 'name1', 'name2' kein einzelner Wert und kann daher nicht in einer vorbereiteten Anweisung verwendet werden).

Sie müssen also Ihre Sichtweise verringern (z. B. indem Sie sehr spezifische, nicht wiederverwendbare Abfragen wie WHERE name IN (?, ?, ?) erstellen) oder gespeicherte Prozeduren nicht verwenden und versuchen, SQL-Injektionen mit anderen Techniken zu verhindern ... 

4
florian h

Sie können TextUtils.join(",", parameters) verwenden, um die sqlite-Bindungsparameter zu nutzen, wobei parameters eine Liste mit "?"-Platzhaltern ist und die Ergebniszeichenfolge etwa "?,?,..,?" lautet.

Hier ist ein kleines Beispiel:

Set<Integer> positionsSet = membersListCursorAdapter.getCurrentCheckedPosition();
List<String> ids = new ArrayList<>();
List<String> parameters = new ArrayList<>();
for (Integer position : positionsSet) {
    ids.add(String.valueOf(membersListCursorAdapter.getItemId(position)));
    parameters.add("?");
}
getActivity().getContentResolver().delete(
    SharedUserTable.CONTENT_URI,
    SharedUserTable._ID + " in (" + TextUtils.join(",", parameters) + ")",
    ids.toArray(new String[ids.size()])
);
1
epool

Ich verwende dazu die Stream-API:

final String[] args = Stream.of("some","data","for","args").toArray(String[]::new);
final String placeholders = Stream.generate(() -> "?").limit(args.length).collect(Collectors.joining(","));
final String selection = String.format("SELECT * FROM table WHERE name IN(%s)", placeholders);

db.rawQuery(selection, args);
0
PPartisan

In Kotlin können Sie joinToString verwenden.

val query = "SELECT * FROM table WHERE name IN (${names.joinToString(separator = ",") { "?" }})"
val cursor = mDb.rawQuery(query, names.toTypedArray())
0

Tatsächlich könnten Sie anstelle von rawQuery die native Abfragemethode von Android verwenden:

public int updateContactsByServerIds(ArrayList<Integer> serverIds, final long groupId) {
    final int serverIdsCount = serverIds.size()-1; // 0 for one and only id, -1 if empty list
    final StringBuilder ids = new StringBuilder("");
    if (serverIdsCount>0) // ambiguous "if" but -1 leads to endless cycle
        for (int i = 0; i < serverIdsCount; i++)
            ids.append(String.valueOf(serverIds.get(i))).append(",");
    // add last (or one and only) id without comma
    ids.append(String.valueOf(serverIds.get(serverIdsCount))); //-1 throws exception
    // remove last comma
    Log.i(this,"whereIdsList: "+ids);
    final String whereClause = Tables.Contacts.USER_ID + " IN ("+ids+")";

    final ContentValues args = new ContentValues();
    args.put(Tables.Contacts.GROUP_ID, groupId);

    int numberOfRowsAffected = 0;
    SQLiteDatabase db = dbAdapter.getWritableDatabase());
        try {
            numberOfRowsAffected = db.update(Tables.Contacts.TABLE_NAME, args, whereClause, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        dbAdapter.closeWritableDB();


    Log.d(TAG, "updateContactsByServerIds() numberOfRowsAffected: " + numberOfRowsAffected);

    return numberOfRowsAffected;
}
0
Stan

Das ist nicht gültig

String subQuery = "SELECT _id FROM tnl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
                "SELECT * FROM table_main where part_of_speech_id IN (" +
                        "?" +
                        ")",
                new String[]{subQuery}););

Das ist gültig

String subQuery = "SELECT _id FROM tbl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
                "SELECT * FROM table_main where part_of_speech_id IN (" +
                        subQuery +
                        ")",
                null);

ContentResolver verwenden

String subQuery = "SELECT _id FROM tbl_partofspeech where part_of_speech = 'noun' ";

final String[] selectionArgs = new String[]{"1","2"};
final String selection = "_id IN ( ?,? )) AND part_of_speech_id IN (( " + subQuery + ") ";
SQLiteDatabase SQLDataBase = DataBaseManage.getReadableDatabase(this);

SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables("tableName");

Cursor cursor =  queryBuilder.query(SQLDataBase, null, selection, selectionArgs, null,
        null, null);
0
Vahe Gharibyan