खंड और प्लेसहोल्डर्स में


104

मैं Android के भीतर निम्नलिखित SQL क्वेरी करने का प्रयास कर रहा हूं:

    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});

हालाँकि, एंड्रॉइड प्रश्न चिह्न को सही मानों से प्रतिस्थापित नहीं करता है। मैं निम्नलिखित कर सकता था, हालाँकि, यह SQL इंजेक्शन से रक्षा नहीं करता है:

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

मैं इस मुद्दे के आसपास कैसे पहुंच सकता हूं और IN क्लॉज का उपयोग कर सकता हूं?

जवाबों:


188

प्रपत्र का एक स्ट्रिंग "?, ?, ..., ?"एक गतिशील रूप से बनाया गया स्ट्रिंग हो सकता है और सुरक्षित रूप से मूल SQL क्वेरी में डाला जा सकता है (क्योंकि यह एक प्रतिबंधित रूप है जिसमें बाहरी डेटा शामिल नहीं है) और फिर प्लेसहोल्डर्स का उपयोग सामान्य रूप से किया जा सकता है।

एक फ़ंक्शन पर विचार करें, String makePlaceholders(int len)जो lenप्रश्न-निशान को अल्पविराम से अलग करता है, फिर:

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);

बस स्थानों के रूप में कई मूल्यों के रूप में ठीक से गुजरना सुनिश्चित करें। SQLite में होस्ट पैरामीटर की डिफ़ॉल्ट अधिकतम सीमा 999 है - कम से कम एक सामान्य बिल्ड में, एंड्रॉइड के बारे में निश्चित नहीं :)

खुश कोडिंग।


यहाँ एक कार्यान्वयन है:

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();
    }
}

8
हां, यह SQLite में पैरामीटर किए गए IN () क्वेरीज़ और बहुत अधिक किसी अन्य SQL डेटाबेस का उपयोग करने का एकमात्र (एकमात्र) तरीका है।
लैरी लस्टिग

इस पद्धति का उपयोग करते हुए, मैंने उपयोग किए गए ContentProvider और क्वेरी में () पद्धति में उपस्थिति के लिए परीक्षण करने के लिए तर्क जोड़ा: "IN?" और अगर पाया जाता है, "की घटना की गिनती करता है?" मूल चयन में, तर्क की लंबाई के साथ तुलना में, एक ",?, ...?" अंतर के लिए और मूल "IN?" की जगह उत्पन्न प्रश्न चिह्न संग्रह के साथ। इससे तर्क लगभग वैश्विक हो जाता है और मेरे उपयोग के लिए यह अच्छी तरह से काम करने लगता है। मुझे सूची में खाली IN फ़िल्टर करने के लिए कुछ विशेष प्रावधान जोड़ना पड़ा, उन मामलों में, "IN?" अब के लिए "1" से बदल दिया गया है।
सैंडविर्म

3
इसके बारे में मूर्खतापूर्ण बात यह है कि, यदि आप एन प्रश्न चिह्नों के साथ अपना स्वयं का स्ट्रिंग बनाने जा रहे हैं, तो आप सीधे डेटा को इनकोड कर सकते हैं। यह मानकर कि यह स्वच्छता है।
क्रिस्टोफर

16
इस पूरे के makePlaceholdersसाथ प्रतिस्थापित किया जा सकता है TextUtils.join(",", Collections.nCopies(len, "?"))। कम क्रिया।
कोनराड मोरावस्की

1
Caused by: android.database.sqlite.SQLiteException: near ",": syntax error (code 1): , while compiling: SELECT url FROM tasks WHERE url=?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?
इमान मराशी

9

लघु उदाहरण, user166390 के उत्तर पर आधारित:

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;
}

5

अफसोस की बात है कि ऐसा करने का कोई तरीका नहीं है (जाहिर 'name1', 'name2'है कि यह एक भी मूल्य नहीं है और इसलिए तैयार किए गए विवरण में इसका उपयोग नहीं किया जा सकता है)।

इसलिए आपको अपनी जगहें कम करनी होंगी (जैसे बहुत विशिष्ट, न कि पुन: प्रयोज्य प्रश्नों को बनाकर WHERE name IN (?, ?, ?)) या संग्रहीत प्रक्रियाओं का उपयोग न करें और कुछ अन्य तकनीकों के साथ SQL इंजेक्शन को रोकने की कोशिश करें ...


4
आप वास्तव में, एक छोटे से काम के साथ, एक क्वेरी में एक पैरामीटर बना सकते हैं। Pst का जवाब, नीचे देखें। परिणामी क्वेरी को मानकीकृत और इंजेक्शन-सुरक्षित है।
लैरी लस्टिग

@LarryLustig आप किस उत्तर का उल्लेख "pst" के उत्तर के साथ कर रहे हैं? क्या इसे हटाया गया? यह यहाँ pst की एकमात्र घटना प्रतीत होती है ...
स्टीफन हाउस्टीन

1
@StefanHaustein " pst का जवाब " (उपयोगकर्ता हटा दिया गया)।
user4157124 21

5

जैसा कि स्वीकृत उत्तर में सुझाया गया है, लेकिन अल्पविराम-अलग करने के लिए कस्टम फ़ंक्शन का उपयोग किए बिना '?'। कृपया नीचे दिए गए कोड की जाँच करें।

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);

1

आप TextUtils.join(",", parameters)sqlite बाइंडिंग मापदंडों का लाभ उठाने के लिए उपयोग कर सकते हैं , जहां प्लेसहोल्डर्स के parametersसाथ एक सूची है "?"और परिणाम स्ट्रिंग कुछ इस तरह है "?,?,..,?"

यहाँ एक छोटा सा उदाहरण है:

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()])
);

क्या होगा अगर "पैरामीटर" में से एक को शून्य होने की आवश्यकता है?
एंड्रॉइड डेवलपर

0

वास्तव में आप कच्चे के बजाय android के देशी तरीके का उपयोग कर सकते हैं:

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

यह मान्य नहीं है

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}););

यह मान्य है

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 का उपयोग करना

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

मैं इसके लिए Streamएपीआई का उपयोग करता हूं :

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);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.