I'm trying to delete a row in a table where I know the row exists. I tried:
final String s = "DELETE from %s where Name = '%s'";
String sql = String.format(s, GetTableName(), sListName);
Cursor cr= GetUserDb().GetSQLiteDb().rawQuery(sql, null);
and I tried:
String sWhere = String.format("Name = '%s'", sListName);
long lRowsUpdated = GetUserDb().GetSQLiteDb().delete(GetTableName(), sWhere, null);
** sWhere >> Name = 'groceries' **
** sql >> DELETE from Lists where Name = 'groceries' **
There is no crash or logcat exception but the row is still not deleted. Is there something I'm missing? Maybe I have a lock on it somewhere else or I need to set certain permissions in my Manifest or something?
Use delete() from SqliteDatabase - this returns the count of affected rows. E.g. delete(tablename, "name=?", new String[] { aString } )
Try this:
SQLiteStatement stmt = db.compileStatement("DELETE FROM " + getTableName() + "WHERE Name=?");
stmt.bindString(1, sListName);
stmt.execute();
if the column you're trying to have your key by is sListName you should have delete where sListName = '%s' unless you have another column that is called Name that you are trying to delete, but if that isn't your primary key you might end up getting two rows in your delete.
You can always use the emulator and an adb shell and just go run the sqlite3 shell command and try your SQL statements. If you type yours in and get 0 rows affected, you know you're statement is messed up, not your java.
If you're not using any built in CurorAdapters or ContentProviders your primary key does not need to be named _ID, it can be named whatever. As for the type, in SQLite3, it's really just a suggestion on how to cast it, you can put whatever data in whatever column you want.
Related
I am building my first android app where I am trying to sync mysql data to sqlite in android. I have two tables in mysql and both gets synced properly into android sqlite. The first table is as follows:
id ProjectName
1 RA_Tesco
2 RA_Coors
3 RA_JWNT
The second table is as follows:
id pid Outlet Add1
1 1 Tesco XYZ
2 1 Tesco ABC
3 2 Coors NBC
The PID in second table references to id of first table. How can I subset the second table based on PID value derived from id of first table. I know it is pretty straight forward in php or mysql or even in Python or R. However, fetching the id based on string and referencing the same in the second table seems quite tricky in Android. My codes so far:
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
clickedId = getIntent().getExtras().get("clickedId").toString();
When I toast clickedId, I get the correct string, for example, RA_Tesco.
cursor = sqLiteDatabase.rawQuery("SELECT * FROM "+SQLiteHelper.TABLE_NAME1+" where pid = 1"+"", null);
The above code also renders the correct set of records from the sqlite table. I am struggling with integrating them both. I tried the following:
String pid;
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
clickedId = getIntent().getExtras().get("clickedId").toString();
pid = sqLiteDatabase.rawQuery( "select id from "+sqLiteHelper.TABLE_NAME+" where projectName = "+clickedId+"", null );
I am getting incompatible types error.
This is what worked for me:
clickedId = getIntent().getExtras().get("clickedId").toString();
cursor = sqLiteDatabase.rawQuery("SELECT * FROM "+SQLiteHelper.TABLE_NAME1+" where pid = (select id from "+SQLiteHelper.TABLE_NAME+ " where ProjectName = '"+clickedId+"'"+")", null);
I just followed the same MySQL principle of nesting queries. The above code roughly reads as follows:
select * from table2 where pid = (select id from table1 where projectname="xyz");
1) Try put your query to single quote
2) rawQuery returns Cursor, not String
So,
Cursor pidCursor = sqLiteDatabase.rawQuery( "select id from "+sqLiteHelper.TABLE_NAME+" where projectName = '"+clickedId+"'", null );
If you want to get the corresponding rows from the 2nd table when you pass as an argumnent the value of a ProjectName (I guess this is clickedId although its name is id?), create a statement like this:
String sql =
"select t2.* from " + SQLiteHelper.TABLE_NAME1 +
" t2 inner join " + SQLiteHelper.TABLE_NAME +
" t1 on t1.id = t2.pid where t1.ProjectName = ?";
This joins the 2 tables and returns all the columns of the 2nd table.
The execute rawQuery() by passing clickedId as a parameter, which is the proper way to avoid sql injection:
Cursor cursor = sqLiteDatabase.rawQuery(sql, new String[] {clickedId});
On devices with modified android versions i get this error. For example on Xiaomi devices.
String query = "select * from dialogues where userId = ? and topChat = 0 order by updatedAtByClient desc";
Cursor dialogRes = db.rawQuery(query, new String[]{userId});
Here i get exception:
android.database.sqlite.SQLiteException: no such column: topChat (code 1):,
while compiling: select * from dialogues where userId = ? and topChat = 0
order by updatedAtByClient desc
I have written the exception message by hand, because the user has sent me it in a screenshot, so there might be typos.
How can this be fixed, and why does this happen?
UPD1:
the create table statement looks similar to this:
"CREATE TABLE IF NOT EXISTS dialogues(fieldName VARCHAR, camelCaseFieldName VARCHAR,
topChat INTEGER, createdAt DATE);";
And i have a correctly implemented update method for when im changing the DB structure, but this particular table and field name did not change for a long time.
UPD2:
i have made an apk for the user with problems, that logs that table columns, and i did see the problematic column in the log, and user says that this version works ok.
So seems that this error does not happen 100% of times. Very strange. Maybe there is a way to check the database for integrity after creating it, and recreate tables with errors?
I don't believe this would be a xiaomi issue. it rather seems be the result of an unfortunate migration, where a new column had not been added and subsequently, the user might still work with the previous version of the table. and there is no other logical explanation for an absent column), simply because either the CREATE TABLE statement works - or it doesn't.
one can still work around it with ALTER TABLE. eg. when that SQLiteException occurs, addColumnIfNotExists("dialogues", "topChat", "INTEGER DEFAULT 0"); ...in order not to cause data-loss by dropping the table, only because it lacks some column.
public void addColumnIfNotExists(String tableName, String columnName, String dataType) {
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName, null);
if(! Arrays.asList(cursor.getColumnNames()).contains(columnName)) {
db.execSQL(String.format("ALTER TABLE %s ADD COLUMN %s %s", tableName, columnName, dataType));
}
} finally {
if(cursor != null) {
cursor.close();
}
}
}
I'm facing an issue with SQLite in Android, I know the solution must be simple, but what I have done is not working !!
// Update a contact with a new name
public void updatename (String phone, String newname) {
newname = newname.replaceAll("'","\'");
String query = "UPDATE contacts SET name = '"+newname+"' WHERE phone = '"+phone+"'";
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL(query);
//db.close();
}
the replace function is not working !!
Use update() to map your strings into placeholders, and Sqlite will escape the strings so that the final command is always valid.
You should always be doing this for every command.
Use PreparedStatement, and should never do the quoting stuff yourself, just unnecessary trouble:
PreparedStatement pstmt = con.prepareStatement("UPDATE constacts SET name = ? WHERE phone = ?");
pstmt.setString(1, "foo")
pstmt.setString(2, '123")
SQL does not use a backslash for escaping.
In SQL string literals, quotes are doubled, so you'd need to replace ' with ''.
But it would be a better idea to use parameters:
String query = "UPDATE contacts SET name = ? WHERE phone = ?";
db.execSQL(query, new Object[]{ newname, phone });
Use Prepared Statements. This sanitizes the input for you before calling the sql command.
How do I use prepared statements in SQlite in Android?
I've tried several methods from here:
SQLite FTS example doesn't work
and here:
Full text search example in Android (best tutorial so far i think)
However, my search returns 0 results!
Here is what I've tried:
String key = "a";
Cursor c = db.query(true, "texts_virtual",
new String[]{"id","title_normalized"},
"title_normalized MATCH '"+key+"'",
null, null, null, null, null);
= 0 Results;
String query = "a";
String[] params = {"%" +query+ "%"};
Cursor c = db.rawQuery("SELECT * FROM texts_virtual WHERE title_normalized MATCH ?", params);
= 0 Results too
I know that the virtual table is correctly working because I can do this:
String queryText = "a"; //here i test other texts and they worked too
String query = "select * from texts_virtual where title_normalized like ? order by number";
String[] params = {"%" + queryText + "%"};
Cursor c = db.rawQuery(query, params);
so this prove that the texts_virtual is working, what is not working are the queries, but I don't know why, not error, nothing, just 0 results.
Also after I make it work, I'm planning to use multiple terms search in 2 columns
user type "WordA WordB WordC"
it search for each word in the 2columns and return the results, but this if for a future task....
Edit
Table Code Creation:
CREATE TABLE texts (id INTEGER PRIMARY KEY AUTOINCREMENT, title_normalized....);
INSERT INTO texts (id, titulo_normalized...) VALUES (1, 'aaaaaa', ...);
and go on for more inserts, and at the end the virtual creation
CREATE VIRTUAL TABLE texts_virtual USING fts4(content="texts", id, title_normalized, ..other fields);
i can query texts_virtual using LIKE but not MATCH, match return 0 results =/
Edit 2 how the table looks:
Table: texts_virtual
----------------------------
id --- title_normalized
--------------------------
1 --- aaaaaaaaab
2 --- abbbbbbbbb
3 --- bbbbbabbbb
4 --- bbbbbbbbbb
The FTS module searches for words (where the exact definition depends on the tokenizer used), or at best for words with a prefix.
MATCH words as designed; it does not find "a" because there is no word "a" in your data.
If you want to find substrings inside words, you must use LIKE.
You are using % as a joker. In FTS requests, You have to use * instead.
LIKE "%word%"
MATCH "*word*"
I've noticed that for very short words (less than 3 letters), LIKE is faster than MATCH. For longer words, MATCH is faster.
I'm working on a conference application where we want the sessions to be first grouped by time and then by room location. I have successfully sorted by one or the other in my ExpandableListActivity, but have been unsuccessful with both the primary and secondary sort.
(source: coreylatislaw.com)
Set up
Custom content provider (extends ContentProvider)
Custom list adapter (extends BaseExpandableListAdapter)
Custom list activity (extends ExpandableListActivity)
Query
Cursor cursor = context.getContentResolver()
.query(uri,
ScheduleData.PROJECTION,
null,
null,
ScheduleData.SORT_ORDER);
Sort order
public static final String SORT_ORDER = TimeSlots.QUALIFIED_TIMESTART + " ASC"; // timeslots.timestart
Failed primary & secondary sort orders
public static final String SORT_ORDER = TimeSlots.QUALIFIED_TIMESTART + " ASC, " + Locations.QUALIFIED_NAME + " ASC"; // timeslots.timestart ASC, locations.name ASC
public static final String SORT_ORDER = TimeSlots.QUALIFIED_TIMESTART + ", " + Locations.QUALIFIED_NAME + " ASC"; // timeslots.timestart, locations.name ASC
The second clause seems to have no affect on the ordering. Is this a limitation of the ExpandableListActivity? Should I specify multiple sort order items differently?
Turns out that there was a comparator in the class that was overriding the sort order specified in the ORDERBY clause. When using the ORDERBY clauses above with out the comparator, I got the desired sorting. You can do it either way, but I'm killing the extra code and choosing the ORDERBY clause.
The comparator path:
// Sort children
Collections.sort(group.children, CHILD_COMPARATOR);
// Specify sort by location
static final Comparator<Child> CHILD_COMPARATOR = new Comparator<Child>()
{
#Override
public int compare (Child child1, Child child2)
{
return child1.location.toLowerCase().compareTo(child2.location.toLowerCase());
}
};
Just get the data into an 'ArrayAdapter', sort it the way you want and then set back the adapter to the activity list.
this is not really an answer, but i cant write comments yet.
The SORT_ORDER String should be correct.
But what you can try is to use the shell and execute the sql lite query directly so that you can narrow down the problem.
start up emulator with your app
in your console type :
adb shell
now navigate to the directory of your app; for example:
cd /data/data/com.myapp/databases)
start the SQLLite command line tool by typing:
sqlite3 < filename >
If you dont know the name of your database file simply type in "ls" do see all files in this directory.
you can now type in your query. For example:
SELECT * FROM timeslots,locations ORDER BY timeslots.timestart, locations.name;
reference:
http://www.sqlite.org/sqlite.html