简体   繁体   中英

How can I use this query with the query method (SQLiteDatabase.query)?

The task I've been given is to write a query to return the number of orphaned rows. I have achieved this but another task is to then not use the rawQuery method to achieve the same result using the query method.

The issue is that I get java.lang.IllegalStateException: Invalid tables

The tables, there are 3 are

  1. the parent table which has an _id column and a name column
  2. the child table which has an _id column, a name column and a childtoparentlink column that is an integer that links to the parent table.
  3. the friend table which has an _id column, a name column and a friendtochildlink column.

The SQL to create and to put rows into the tables, including some orphans is like

CREATE TABLE parent(_id INTEGER PRIMARY KEY,parentname TEXT);
CREATE TABLE child(_id INTEGER PRIMARY KEY,childname TEXT, childtoparentlink INTEGER);
CREATE TABLE friend(_id INTEGER PRIMARY KEY,friendname TEXT, friendtochildlink INTEGER);
INSERT INTO parent VALUES(null,'Parent A');
INSERT INTO parent VALUES(null,'Parent B');
INSERT INTO child VALUES(null,'Child A',1);
INSERT INTO child VALUES(null,'Child B',2);
INSERT INTO child VALUES(null,'Child X',10); -- orphan
INSERT INTO friend VALUES(null,'Friend A',1);
INSERT INTO friend VALUES(null,'Friend B',2);
INSERT INTO friend VALUES(null,'Friend X',100); -- orphan

The query that works and gives the right values when using rawQuery is

SELECT 
    (
        SELECT count() FROM child 
            LEFT JOIN parent ON child.childtoparentlink = parent._id 
        WHERE parent.parentname IS NULL
    ) AS child_mismatches,
    (
        SELECT count() FROM friend 
            LEFT JOIN child ON friend.friendtochildlink = child._id 
        WHERE child.childname IS NULL
    ) AS friend_mismatches
  • I get two columns each with a value of 1 (as wanted).

My actual code is :-

public ArrayList<String> checkLinkIntegrity() {
    ArrayList<String> return_value = new ArrayList<>();
    String suffix = "_mismatches";
    String child_result_cl = TB_CHILD + suffix;
    String sq_child_mismatches = "(SELECT count() FROM " +
            TB_CHILD +
            " LEFT JOIN " + TB_PARENT +
            " ON " + TB_CHILD + "." + CL_CHILDTOPARENTLINK + " = " +
            TB_PARENT + "." + CL_PARENTID +
            " WHERE " + TB_PARENT + "." + CL_PARENTNAME + " IS NULL)" +
            " AS " + child_result_cl;
    String friend_result_cl = TB_FRIEND + suffix;
    String sq_friend_mismatches = "(SELECT count() FROM " +
            TB_FRIEND +
            " LEFT JOIN " + TB_CHILD +
            " ON " + TB_FRIEND + "." + CL_FRIENDTOCHILDLINK + " = " +
            TB_CHILD + "." + CL_CHILD_ID +
            " WHERE " + TB_CHILD + "." + CL_CHILDNAME + " IS NULL)" +
            " AS " + friend_result_cl;
    String full_query = "SELECT " + sq_child_mismatches + "," + sq_friend_mismatches;
    SQLiteDatabase db = this.getWritableDatabase();
    Cursor csr;
    Log.d("RAWQUERYSQL",full_query);
    csr = db.rawQuery(full_query,null);
    return_value.addAll(dumpCursorToStringArrayList(csr,"RAWQUERY"));

    // Fails invalid table
    csr = db.query(null,new String[]{sq_child_mismatches,sq_friend_mismatches},null,null,null,null,null);
    return_value.addAll(dumpCursorToStringArrayList(csr,"SECONDTRY"));
    csr.close();
    return return_value;
}

and the dumpCursortoStringArrayList method is :-

private ArrayList<String> dumpCursorToStringArrayList(Cursor csr, String tablename) {
    ArrayList<String> rv = new ArrayList<>();
    int original_position = csr.getPosition();
    csr.moveToPosition(-1);
    rv.add("Table: " + tablename);
    while (csr.moveToNext()) {
        rv.add("\tRow # " + String.valueOf(csr.getPosition() + 1));
        for (String column: csr.getColumnNames()) {
            rv.add("\t\tColumn: " + column + "\tvalue is: \t" + csr.getString(csr.getColumnIndex(column)));
        }
    }
    csr.moveToPosition(original_position);
    return rv;
}

I get the same error if I try "" instead of null eg

If I use only the rawQuery I get

04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS: Table: RAWQUERY
04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS:  Row # 1
04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS:      Column: child_mismatches    value is:   1
04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS:      Column: friend_mismatches   value is:   1

This is from using

    ArrayList<String> results = DBOpenHelper.checkLinkIntegrity();
    for (String s : results) {
        Log.i("RESULTS",s);
    }

How can I run the query with the query method instead of the rawQuery method, to get the better marks?

Your issue is that the query method expects a table as it then generates SQL as per

SELECT your_columns FROM the_table; 

As there is no table it issues the Invalid Table exception.

You have to provide something that will satisfy the FROM clause, it cannot be nothing.

You can get around this in a few ways, which I guess is what the homework is trying to get you to ascertain/explore.

Fix 1

You could supply one of the tables that exist eg use

csr = db.query(null,new String[]{sq_child_mismatches,sq_friend_mismatches},null,null,null,null,null,"1");
  • Note the 8th parameter, this LIMIT s the number of rows generated to one as there would be a row generated for each row in the fake table.

Fix 2

or as FROM can be a subquery (see diagram) you could use a subquery eg one that you have 在此输入图像描述

So you could use :-

    csr = db.query(
            sq_child_mismatches, //<<<<<<<<<< the fake subquery
            new String[]{
                    sq_child_mismatches,
                    sq_friend_mismatches
            },
            null    ,null,null,null,null
    );
  • In this case, as the fake subquery returns a single row/result, there is no need for the LIMIT clause.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM