Android SQLite多条件查询异常:AND/OR组合返回结果不符合预期
问题排查与修复方案
嘿,我一眼就瞅出问题所在了——SQL逻辑运算符的优先级搞混了你的查询条件!
你原本的SQL语句逻辑因为运算符优先级被篡改了:
SELECT * FROM TABLE_NAME WHERE COLUMN_NAME_COUNTRY='xxx' AND COLUMN_NAME_CATEGORY='Local' OR COLUMN_NAME_CATEGORY='NA'
由于AND的优先级高于OR,数据库实际执行的逻辑是:
(匹配指定国家 AND 类别为Local) OR 类别为NA
这就导致两个不符合预期的结果:
- 所有类别为
NA的数据,不管所属国家是什么,都会被查出来 - 若测试数据中
NA类别的数据较少,就会呈现出“所有国家的Local类别数据都被返回”的现象——本质是国家筛选条件没有作用到Local类别的全局匹配上
正确的逻辑应该是:指定国家的数据,且类别是Local或NA,所以必须用括号把OR的两个类别条件包裹起来,让数据库先判断类别,再和国家条件做AND运算:
SELECT * FROM TABLE_NAME WHERE COLUMN_NAME_COUNTRY='xxx' AND (COLUMN_NAME_CATEGORY='Local' OR COLUMN_NAME_CATEGORY='NA')
另外,你的代码还有个大隐患:直接拼接字符串生成SQL会导致SQL注入攻击!如果传入的country参数包含单引号(比如O'Neil),会直接导致SQL语法错误,甚至被恶意利用。所以必须改用参数化查询。
修复后的完整代码
public List<AppDataBean> getAllDataForSelectedCountryWithCategory(String country) { ArrayList<AppDataBean> dataList = new ArrayList<>(); SQLiteDatabase db = AppDbHelper.getInstance(context).getWritableDatabase(); dataList.clear(); try { // 修正SQL逻辑:用括号包裹OR条件,同时用?占位符实现参数化查询 String sql = "SELECT * FROM " + AppDbConstructor.AppData.TABLE_NAME + " WHERE " + AppDbConstructor.AppData.COLUMN_NAME_COUNTRY + "=?" + " AND (" + AppDbConstructor.AppData.COLUMN_NAME_CATEGORY + "=? OR " + AppDbConstructor.AppData.COLUMN_NAME_CATEGORY + "=?)"; // 传入参数数组,避免SQL注入,同时自动处理字符串转义 Cursor c = db.rawQuery(sql, new String[]{country, "Local", "NA"}); // 简化Cursor遍历:用do-while更直观,无需提前判断count if (c.moveToFirst()) { do { AppDataBean appDataBean = new AppDataBean(); appDataBean.setCategory(c.getString(c.getColumnIndex(AppDbConstructor.AppData.COLUMN_NAME_CATEGORY))); appDataBean.setUrl(c.getString(c.getColumnIndex(AppDbConstructor.AppData.COLUMN_NAME_URL))); appDataBean.setSource(c.getString(c.getColumnIndex(AppDbConstructor.AppData.COLUMN_NAME_SOURCE))); appDataBean.setIconUrl(c.getString(c.getColumnIndex(AppDbConstructor.AppData.COLUMN_NAME_ICON_URL))); appDataBean.setCountry(c.getString(c.getColumnIndex(AppDbConstructor.AppData.COLUMN_NAME_COUNTRY))); Log.d("APP_DB_SOURCE","COLUMN_NAME_SOURCE: "+ c.getString(c.getColumnIndex(AppDbConstructor.AppData.COLUMN_NAME_URL)) + ", " + c.getString(c.getColumnIndex(AppDbConstructor.AppData.COLUMN_NAME_CATEGORY))); dataList.add(appDataBean); Log.d("APPDBSOURCE", "LIST_VALUES_ARE" + dataList.get(dataList.size()-1).getCategory() + ", " + dataList.get(dataList.size()-1).getCountry()); } while (c.moveToNext()); } c.close(); } catch (Exception e) { e.printStackTrace(); } finally { db.close(); } return dataList; }
几个关键的改进点
- 修正SQL逻辑:通过括号强制改变运算顺序,确保国家筛选条件同时作用于Local和NA两个类别
- 参数化查询:用
?作为占位符,传入参数数组,彻底避免SQL注入,同时解决特殊字符转义问题 - 简化遍历逻辑:用
do-while替代原有的for循环,不需要提前获取游标count,代码更简洁高效
内容的提问来源于stack exchange,提问作者Nuwan Withanage
相关产品推荐
相关产品推荐

