I have two tables, created by the following statements:
create table maintable (_id integer primary key, name)
create table foreigntable (_id integer primary key, object, foreign key (object) references maintable(_id)
I'd like to query foreigntable for all items referencing a specific object in maintable. Let's say the id of interest in maintable is stored in objectId.
Why does the following code return no results?
Cursor dataCursor = database.query("foreigntable", null, "object=?", new String[] { Long.toString(objectId)}, null, null, null);
I get results with the following query:
Cursor dataCursor = database.rawQuery("select * from foreigntable where object=" + objectId, null);
What also works:
Cursor dataCursor = database.query("foreigntable", null, "object="+objectId, null, null, null, null);
"object=?", new String[] { Long.toString(objectId)}
This code compares the values in the object column against a string.
This comparison will always fail because numbers are not strings.
While using parameters is a good idea in general, Android's database API allows nothing but strings, and when you have a number, you should insert it directly into the SQL query string.
Related
I'm trying to select some data from database and I have two slices of code to do it:
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"id = ?", new String[]{getSID(db)}, null, null, null);
and
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"id = " + getSID(db), null, null, null, null);
The difference between them is that first one seems to be more correct according to documentation, but it also doesn't work - cursor is empty. Instead of the second one - I'm getting all data I need.
So I tried to execute different SQL queries on my PC with a copy of database and that's what I've got:
SELECT col1, col2, col3 FROM SomeTables WHERE (id = '42')
This one doesn't work (and this query obviously equals to query, generated by first code sample)
SELECT col1, col2, col3 FROM SomeTables WHERE (id = 42)
And this one works fine (equals to query from second code sample).
As I know, SQLite should perform type cast automatically, but something went wrong and I don't know why. Do you have any ideas about how first code sample can be fixed? (Or, perhaps, database?)
If it matters, here's simplified CREATE script of the table with id field:
CREATE TABLE SomeTable ( ID PRIMARY KEY, col1, col2, [...] )
UPD: And, by the way, getSID(db) returns String Object.
That query parameters can only be strings is a horrible design error in the Android database API.
Despite what the documentation says, you should use parameters only for actual string values; integer values can be safely embedded directly into the SQL string. (For blobs, you must use a function that accepts ContentValues.)
Please note that while SQLite uses dynamic typing, values of different types do not compare equal in most cases (SELECT 42='42'; returns 0).
There are some cases where SQLite does automatically convert values due to type affinity (in your case, this would happen if you declared the id column as INTEGER), but this is rather counterintuitive, so it should not be relied upon.
According to SQLite documentation,
Any column in an SQLite version 3 database, except an INTEGER PRIMARY KEY column, may be used to store a value of any storage class.
In context of my case, that means that we can't be sure what data type will be stored in columns. If you can control and convert data types when they're putting into database - you can convert id values to TEXT when adding data to database and use selectionArgs easily. But it's not an answer for my question, because I have to deal with database content as is.
So, possible solutions:
a) embed integer values in selection string without wrapping them into ':
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"id = " + getSID(db), null, null, null, null);
b) cast values from selectionArgs: CAST(? as INTEGER) or CAST(id AS TEXT). I think, converting column to TEXT is better solution, because right operand is always TEXT, but the left one can be anything. So:
cursor = builder.query(db,
new String[]{"col1", "col2", "col3"},
"CAST(id AS TEXT) = ?",
new String[]{getSID(db)}, null, null, null);
You need to convert your int id into string before passing to your query because the parameter array is of type string. For example:
cursor = builder.query(db, new String[]{"col1", "col2", "col3"},
"id = ?", new String[]{String.valueOf(getSID(db))}, null, null, null);
The reason why it works in second type of query is because you are appending the integer value with string which automatically converts the int into String. For example:
int i = 10;
String s = i + ""; //now 10 is in string
I have a table with 10 columns.
String createQuery = " CREATE TABLE IF NOT EXISTS profile (
_id integer primary key autoincrement,
name text,
longi real,
lati real,
vibration integer,
sound integer,
brightness integer,
mdata,
bluetooth,
wifi);";
How can I get all table data in an ArrayList?
You need to do a few things. You need to create a sub-class of SQLiteDatabase. Once you have that, you can get run a query inside a method in this class like this:
Cursor cursor = getReadableDatabase().query("profile", // table name
new String[] { // columns
"_id",
"name",
"longi",
"lati",
"vibration",
"sound",
"brightness",
"mdata",
"bluetooth",
"wifi"
},
null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Long id = cursor.getLong("_id");
String name = cursor.getString("name");
// and so on, list of your columns you want to get.
cursor.moveToNext();
}
Obviously you won't be able to put one whole row into an array list because you have different column types. But you can process one row at a time inside the while loop after you get all the values.
Most of this info is taken from here, which is a great source of info:
http://www.vogella.com/articles/AndroidSQLite/article.html
I want to enter name and phone number from two edit text.i use two buttons to save and show it in emulator using list view.After entering name and when i click save button how to check whether i have already entered the same name. i am new to android explanation will be really helpful.
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+tbname+"("+Key_id+" INTEGER PRIMARY KEY AUTOINCREMENT, "+Key_name+" TEXT,"+Key_mobile+" TEXT)");
}
public void n(String aa, String bb) {
SQLiteDatabase db=this.getWritableDatabase();
ContentValues cv=new ContentValues();
cv.put(Key_name, aa);
cv.put(Key_mobile, bb);
db.insert(tbname, Key_name, cv);
db.close();
}
public Cursor cr()
{
SQLiteDatabase db=getReadableDatabase();
String [] colms=new String[]{Key_id+" as _id",Key_name,Key_mobile};
Cursor cur=db.query(tbname, colms, null, null, null, null, null);
cur.moveToFirst();
return cur;
}
I would start with changing your table definition by adding the NOT NULL and UNIQUE constraints.
db.execSQL("CREATE TABLE "+tbname+"("+Key_id+" INTEGER PRIMARY KEY AUTOINCREMENT, "+Key_name+" TEXT NOT NULL UNIQUE,"+Key_mobile+" TEXT)");
Then you have a choice of methods to use for your insert. You can use:
insertOrThrow will return the id of your new record, or -1 on an error (and a constraint failure of not having a unique name would be an error).
insertWithOnConflict will return the id of the new record OR the primary key of the existing row if the input param 'conflictAlgorithm' = CONFLICT_IGNORE OR -1 if any error.
Personally, I would use insertWithOnConflict with the CONFLICT_IGNORE flag set. That way you can get the row id back for the duplicate record (as well as not letting the duplicate get entered).
Put UNIQUE in your table field definition an then use insertOrThrow. If you insert the same, insertOrThrow will cause an exception, you can intercept it.
I'm creating an app as a learning tool and am having difficulty with join queries.
I have a database with two tables- horses and covers- declared as follows;
private static final String HORSES_CREATE = "create table horses (_id integer primary key autoincrement, "
+ "name text not null, type integer not null, birthDate text not null, vaccineDate text not null, "
+ "inFoal integer not null, notes text not null);";
The 'type' field refers to stallion, mare, gelding etc and is selected from a spinner (populated from an XML String array).
private static final String COVERS_CREATE = "create table covers (_id integer primary key autoincrement, "
+ "stallionName integer not null, mareName integer not null, firstCoverDate text not null, lastCoverDate text not null, "
+ "scan14Date text not null, scan28Date text not null, foalingDate text not null, inFoal integer not null, notes text not null);";
stallionName is actually stored as the _id field of the horse from the horse table. It is selected from a spinner that only displays horses whose type defined as 'Stallion' in the horses table. (The same applies for Mare).
I have a class 'DatabaseHelper' to create and upgrade the tables, and each table has its own adapter class 'horsesDbAdapter' and 'coversDbAdapter' that contains the methods to add, edit and delete entries, and relevant queries. (fetchAllHorses(), fetchHorse(long rowId) )
eg:
public Cursor fetchAllHorses() {
return mDb.query(DATABASE_TABLE,
new String[] { KEY_ROWID, KEY_NAME, KEY_TYPE, KEY_BIRTHDATE,
KEY_VACCINEDATE, KEY_INFOAL, KEY_NOTES }, null, null,
null, null, null);
}
(It's all adapted from the Android notepad example)
I have the contents of the covers table displayed in a listview (just showing the stallionName and mareName). But as those fields just contain the unique reference to the horses table all that is displayed is the fairly uninformative _id field.
My question is; how can I get the relevant name for the horses to display in the listView? I've read up on join queries etc but get lost when I try implement them. I assume I have to join on horses._id and covers.stallionName (then make an almost-identical one for MareName) but I can't find a concrete example of how to do this.
Please let me know if any additional information/ code is needed.
Any help would be greatly appreciated, thankyou in advance.
EDIT:
I have made stallionName and mareName foreign keys referencing (_id) in the horses table, but am still unsure how and where to implement the join query; should it be in the horsesDbAdapter, coversDbAdapter or the coversList class? (coversList is the class that creates and populates the listView)
The covers table declaration now reads;
private static final String COVERS_CREATE = "create table covers (_id integer primary key autoincrement, "
+ "stallionName integer not null, mareName integer not null, firstCoverDate text not null, lastCoverDate text not null, "
+ "scan14Date text not null, scan28Date text not null, foalingDate text not null, inFoal integer not null, notes text not null," +
"FOREIGN KEY (stallionName) REFERENCES horses (_id), FOREIGN KEY (mareName) REFERENCES horses (_id));";
I'm a newbie to Android and SQLLiteHelper and was wondering about the same thing.
After reading this post, I found that the method to define joins between tables is done with the SQLLiteQueryBuilder.setTables method. See the reference here
I got this from this blog
Hope this helps pointing readers in the right direction.
I've managed to get it working. For anyone else who may have a similar problem I'll try detail what I did.
As #deceiver stated I should have made stallionName and mareName foreign keys referencing horses (see Edit).
In the coversList class (the class that implements the listView) I just needed to get an instance of the database and use a rawQuery to implement the SQL code directly (It may be possible to do it with Query but I'm not sure how)
The added code is as follows;
private Cursor coversCursor;
private void fillData() {
db = (new DatabaseHelper(this).getReadableDatabase());
coversCursor = db.rawQuery("SELECT horses1.name AS stallionNameText, covers.*, horses2.name AS mareNameText FROM horses horses1 JOIN covers ON horses1._id = covers.stallionName JOIN horses horses2 ON horses2._id = covers.MareName",
null);
startManagingCursor(coversCursor);
// Create an array to specify the fields we want to display in the list
// (the renamed fields from the above query)
String[] from = new String[] { "stallionNameText", "mareNameText" };
// and an array of the fields we want to bind those fields to (in this
// case just text1)
int[] to = new int[] { R.id.rowStallionName, R.id.rowMareName };
// Now create a simple cursor adapter and set it to display
SimpleCursorAdapter covers = new SimpleCursorAdapter(this,
R.layout.covers_list_row, coversCursor, from, to);
setListAdapter(covers);
}
FillData() is then called in the onCreate() method. Then just close the db in the onDestroy method.
(Selecting all columns from the covers table seems wasteful here but I will eventually show these columns in the listView aswell. I just wanted to answer this before continuing coding).
A tutorial I found helpful was http://coenraets.org/blog/android-samples/androidtutorial/
I had issues with the SQL query as there are 2 foreign keys referencing the same table and I wanted the listView to display both the stallion name and mare name, so had to join two horses table to a covers table. I just needed to rename the tables in the FROM section of the SQL query. Hopefully the above code is clear. If not, I found the following useful; http://www.bryantwebconsulting.com/blog/index.cfm/2005/3/11/join_a_table_to_itself_in_sql
Sorry if this explanation is too specific to my (unusual) example.
Thanks for reading.
This is my table apTable:
db.execSQL("CREATE TABLE "+apTable+"
("
+idAP+" TEXT PRIMARY KEY , "
+ssID+" TEXT, "
+testAPid+" INTEGER NOT NULL ,FOREIGN KEY ("+testAPid+") REFERENCES "+testsTable+" ("+colTestID+") ON DELETE CASCADE);");
I want to make a query that will give me the idAP rows where, idAP equals a given value and where the foreign key equals another given value.
I'm doing it like this, but it isn't working:
public String CheckAP(String BSSID, String Teste)
{
SQLiteDatabase db=this.getReadableDatabase();
String[] params=new String[]{BSSID, Teste};
Cursor c=db.rawQuery("SELECT "+idAP+" FROM "+apTable+" WHERE "+idAP+"="+BSSID+" AND "+testAPid+"="+Teste, null);
c.moveToFirst();
int index= c.getColumnIndex(idAP);
return c.getString(index);
}
What is the problem here?
Refer to this.
Use a database openhelper.
http://www.screaming-penguin.com/node/7742
And use this to check if you have created your database.
http://androidblogger.blogspot.com/2009/06/tutorial-how-to-access-android-database.html
try this? think the built-in query works just without having to put in the where clause
Cursor c=db.query(apTable, idAP, idAP+"="+BSSID+" AND "+testAPid+"="+Teste, null, null, null, null);
but why would you want to select idAP if you already know you are selecting a specific idAP? shouldnt you be doing something like this?
Cursor c=db.query(apTable, idAP, testAPid+"="+Teste, null, null, null, null);
:S