IN子句和占位符

我正在尝试在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}); 

但是,Android不会使用正确的值替换问号。 我可以做到以下,但是,这不防止SQL注入:

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

我怎样才能解决这个问题,并能够使用IN子句?

一个forms为"?, ?, ..., ?"字符串 可以是一个动态创建的字符串,并安全地放入原始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 – 至少在正常的版本,不知道关于Android 🙂

快乐的编码。


这是一个实现:

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

简短的例子,根据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; } 

可悲的是,没有办法做到这一点(显然是'name1', 'name2'不是一个单一的值,因此不能用在一个准备好的声明)。

所以你将不得不降低你的视野(例如通过创建非常具体的,不可重用的查询,如WHERE name IN (?, ?, ?) )或不使用存储过程,并尝试防止SQL注入与其他技术…

正如接受的答案中的建议,但不使用自定义函数来生成逗号分隔的“?”。 请检查下面的代码。

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

您可以使用TextUtils.join(",", parameters)来利用sqlite绑定参数,其中parameters是一个列表"?" 占位符和结果字符串就像"?,?,..,?"

这是一个小例子:

 Set positionsSet = membersListCursorAdapter.getCurrentCheckedPosition(); List ids = new ArrayList<>(); List 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()]) ); 

其实你可以使用android的原生查询方式,而不是rawQuery:

 public int updateContactsByServerIds(ArrayList 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; } 

这不是有效的

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

在Kotlin中,你可以使用joinToString

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

我面临同样的问题,我认为接受的答案非常复杂。 我更喜欢以下内容:

 String names = "'name1', 'name2'"; // in the code this is dynamically generated String query = "SELECT * FROM table WHERE name IN (" + names + ")"; Cursor cursor = mDb.rawQuery(query, null);