1
public void DBSearchCategory(String tableName) {
    // 1st way 
    String inClause = s1.ListViewCategory.toString();
    inClause = inClause.replace("[", "(");
    inClause = inClause.replace("]", ")");

   //        Cursor cursor = database.rawQuery("SELECT CATEGORY FROM " + tableName
  //                + " WHERE CATEGORY NOT IN " + inClause
 //                + " ORDER BY RANDOM() LIMIT 1 ", null);

    // 2nd way
    try {
        StringBuilder sb = new StringBuilder("");
        for (String param : s1.ListViewCategory) {
            sb.append(",").append('"').append(param).append('"');
        }
        params = sb.toString().substring(1); 
        Log.v("Tag", "params value is " + params);
    } catch (StringIndexOutOfBoundsException e) {

    }
    Cursor cursor = database.rawQuery("SELECT CATEGORY FROM " + tableName
                    + " WHERE CATEGORY NOT IN (?) "
                    + " ORDER BY RANDOM() LIMIT 1 ", new String[]{params});

    while (cursor.moveToNext()) {
        category = cursor.getString(cursor.getColumnIndex("CATEGORY"));
        s1.keyCategory = category;
    }
    cursor.close();
}
  1. s1.ListViewCategory is a String type ArrayList in Singleton class s1, and it has values of categories: "game","country","city","subway","actor","pet" // In Database there are total 33 categories, and I want to exclude these 6 categories that are in s1.ListViewCategory

  2. In rawQuery, I want to exclude categories that are in s1.ListViewCategory, so I tried 2 ways of cursor refering to these 2 stackoverflow questions: Android - sqlite in clause using string values from array? ///Android - sqlite in clause using values from array

I used WHERE and NOT IN statement to exclude these 6 categories

  1. When I tried 2nd way cursor, I got no error. However, the Sql query did not work. It had to exclude categories that are in String[params], but it did not work. So I used log to see what param is and I got this

    2020-01-09 09:16:47.233 8978-8978/com.kj.word V/Tag: params value is "game","country","city","subway","actor","pet"

  2. When I tried 1st Cursor Category, I got error logcat:

    Error Code : 1 (SQLITE_ERROR)
    Caused By : SQL(query) error or missing database.
    (no such column: game (code 1): , while compiling: SELECT CATEGORY FROM KeyWordDB WHERE CATEGORY 
    NOT IN (game, country, city, subway, actor, pet) ORDER BY RANDOM() LIMIT 1)
    #################################################################
    at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
    at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1008)
    at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:573)
    at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
    at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:59)
    at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
    at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
    at android.database.sqlite.SQLite
    

I confirmed that there is a database, so I guess it is probably sql query problem ...

  1. So my question is How can I fix 1st or 2nd cursor to exclude categories that are in s1.ListViewCateogry?

I've searched real hard, but I wasn't able to find answer... Ill be real grateful, if someone answers this question

forpas
  • 160,666
  • 10
  • 38
  • 76
Korea19800
  • 59
  • 4

1 Answers1

1

Change the double quotes with single quotes inside the loop that constructs the comma delimited list:

for (String param : s1.ListViewCategory) {
    sb.append(",").append("'").append(param).append("'");
}
params = sb.toString().substring(1); 

This code constructs a list like:

'game', 'country', 'city', 'subway', 'actor', 'pet'

If you use it as a parameter in the rawQuery() method then this list will be treated as a string literal and not a list of values.
So do this instead:

String sql = "SELECT CATEGORY FROM " + tableName
             + " WHERE CATEGORY NOT IN (?) "
             + " ORDER BY RANDOM() LIMIT 1 ";
sql = sql.replace("?", params);
Cursor cursor = database.rawQuery(sql, null);

Note that this method is prone to sql injection.

Another way is to create a list of ? placeholders instead of 1 placeholder and pass the list of values as an array of strings like this:

for (String param : s1.ListViewCategory) {
    sb.append(",?");
}
String[] array = ListViewCategory.toArray(new String[s1.ListViewCategory.size()]);

params = sb.toString().substring(1);
String sql = "SELECT CATEGORY FROM " + tableName
           + " WHERE CATEGORY NOT IN (@) "
           + " ORDER BY RANDOM() LIMIT 1 ";
sql = sql.replace("@", params);

Cursor cursor = database.rawQuery(sql, array);
forpas
  • 160,666
  • 10
  • 38
  • 76
  • I just tried, but it still doesn't work... What could I do? – Korea19800 Jan 09 '20 at 08:26
  • 1
    Remopve the try/catch block to check if there is any error. Also what is the result of: `Log.v("Tag", "params value is " + params);`? – forpas Jan 09 '20 at 08:28
  • 2020-01-09 09:16:47.233 8978-8978/com.kj.word V/Tag: params value is "game","country","city","subway","actor","pet" – Korea19800 Jan 09 '20 at 10:52
  • 1
    @Korea19800 see another safer way to do it in my edited answer. – forpas Jan 09 '20 at 11:44
  • 1
    Thanks a lot! I will try and ask again if it has errors. Also I will select your answer if there is no error! Thanks a lot again – Korea19800 Jan 09 '20 at 13:20
  • I think your safer way works 90%, 1 time per 10 trial it doesn't work. the query does not excludes words in Array 1 time per 10 trial – Korea19800 Jan 10 '20 at 05:33
  • when the query doesnt work it shows same category such as "pet". – Korea19800 Jan 10 '20 at 05:36
  • It means, The query sometimes excludes the categories in s1.ListViewCategory, but sometimes it does not excludes categories in s1.ListViewCategory – Korea19800 Jan 10 '20 at 08:25
  • 1
    The statement uses `NOT IN` which means it excludes **everything** in the list (which must have at least 1 item, right?). So either it works or not. Did you try both ways? – forpas Jan 10 '20 at 08:28
  • Yeah, it should have exlcuded when there is at least 1 item at Array, but as I mentioned it does not work 2 or 3 times per 10 times trial – Korea19800 Jan 10 '20 at 08:29
  • 1
    I can't reproduce your issue. You will have to debug to check the sql statement right after: `sql = sql.replace("?", params);` and log its value if it is as it should be. – forpas Jan 10 '20 at 08:32
  • 1
    Hey man finally I fixed my problem. It was a DB problem, I inserted category name "videogame" in DB and wrote "video game" in ListView. That space bar made the query not excluding videogame. Anyway, I really appreciate your help – Korea19800 Jan 10 '20 at 10:06
  • forpas If you don't mind would you answer my last question? When s1.ListViewCategory has no category, which means null, I got this error Logcat: Caused by: java.lang.NullPointerException: replacement == null at java.lang.String.replace(String.java:2187) at WordDBRecord999.DBSearchCategory(WordDBRecord999.java:611) at RoleSelection07.onCreate(RoleSelection07.java:29) at android.app.Activity.performCreate(Activity.java:6955) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126) – Korea19800 Jan 10 '20 at 11:24
  • How can I solve this problem? When s1.ListViewCategory is null? – Korea19800 Jan 10 '20 at 11:28
  • 1
    Before anything, check the size of the list. Something like: `if (s1.ListViewCategory.size() > 0) {....} else {....}`. In the if block use the sql statement as the code in my answer and in the else part use the sql statement without the `WHERE CATEGORY NOT IN ...` because you want all the rows of the table and don't pass any parameters. – forpas Jan 10 '20 at 11:38
  • 1
    Also, why is s1.ListViewCategory null? It should not be null but empty if you don't have any items to check. – forpas Jan 10 '20 at 11:49
  • 1
    Thanks a lot.. I finally achieved what I wanted – Korea19800 Jan 10 '20 at 13:55