I'm having some issues working with a CursorAdapter.
In bindView(), I retrieve data in this way:
final String id = c.getString(c.getColumnIndexOrThrow(MySQLiteHelper.PROF_CONTACTS_KEY_ID));
final String name = c.getString(c.getColumnIndexOrThrow(MySQLiteHelper.PROF_CONTACTS_KEY_NAME));
Right after this code, I call
Log.e("Log",id+" <=> "+name);
But, because of some weird problem, I got as a result an ID moved forward by 1.
This is the situation in the DB (pulling it from the emulator, and opening it with SQLite Manager):
And this is the output:
With bigger numbers (>9), IDs start to mess even more up: number 10 becomes number 1, number 13 becomes number 5, etc.
I wouldn't have a lot of problems, in fact the only thing not matching is the id, all other info correspond, but I have a details activity to which I pass the ID in order to show to the user the detailed info.
This is the piece of code where I apply the adapter:
mCursor = mDb.rawGet("SELECT * FROM "+MySQLiteHelper.PROF_CONTACTS_TB_NAME+" LEFT JOIN "+
MySQLiteHelper.EXAMS_TB_NAME+" ON "+
MySQLiteHelper.PROF_CONTACTS_TB_NAME+"."+MySQLiteHelper.PROF_CONTACTS_KEY_COD_ESAME+"="+
MySQLiteHelper.EXAMS_TB_NAME+"."+MySQLiteHelper.EXAMS_KEY_COD
+ " ORDER BY " + MySQLiteHelper.PROF_CONTACTS_TB_NAME+"."+MySQLiteHelper.PROF_CONTACTS_KEY_ID);
if (mCursor.getCount() == 0) {
// error stuff.
} else {
String[] columns = new String[] {};
int[] to = new int[] {};
mDataAdapter = new CursorAdapterProfContacts(getSherlockActivity(), R.layout.item_prof_contact, mCursor, columns, to, 0);
mLvContacts.setAdapter(mDataAdapter);
}
Move the cursor to the first row,after initial cursor like,
mCursor.moveToFirst()
Are you sure that you have _id correctly populated when you insert a value? You can extract the database if you use the emulator and open it with SQLiteManager plugin for Firefox. As well, instead of quering all with *, use the same projection column names as you use inside y our bindView(); something is not matching here
It was due to a collision name: _id can be referred both to EXAMS and PROF. SQLlite chose EXAMS instead of PROF.
mCursor = mDb.rawGet("SELECT *, "+
MySQLiteHelper.PROF_CONTACTS_TB_NAME+"."+MySQLiteHelper.PROF_CONTACTS_KEY_ID+" AS idProf "+
" FROM "+MySQLiteHelper.PROF_CONTACTS_TB_NAME+" LEFT JOIN "+
MySQLiteHelper.EXAMS_TB_NAME+" ON "+
MySQLiteHelper.PROF_CONTACTS_TB_NAME+"."+MySQLiteHelper.PROF_CONTACTS_KEY_COD_ESAME+"="+
MySQLiteHelper.EXAMS_TB_NAME+"."+MySQLiteHelper.EXAMS_KEY_COD +
" ORDER BY " + MySQLiteHelper.PROF_CONTACTS_TB_NAME+"."+MySQLiteHelper.PROF_CONTACTS_KEY_ID);
And finally
final Long id = c.getLong(c.getColumnIndexOrThrow("idProf"));
This made the trick.
Collision name errors should be thrown, as it is in SQL and MySQL.
Related
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 am developing an android app where I want to delete the last row in one of my database table. I have tried the code below, but its throwing a syntax error.
public void deletelatestprofilefromsystemsettings()
{
String maxid = System_id + "="+"SELECT MAX ("+System_id+") FROM" +TABLE_SYSTEM_SETTINGS;
getWritableDatabase().delete(TABLE_SYSTEM_SETTINGS, maxid ,null);
}
Please help! Thanks!
You are lacking a space after the FROM, and subqueries must be written in parentheses:
String maxid = System_id + "=" +
"(SELECT MAX("+System_id+") FROM " + TABLE_SYSTEM_SETTINGS + ")";
You are trying to execute a DELETE with a SELECT in the same query. AFAIK you shouldn't do it. You have to execute the SELECT query first, in order to retrieve the desired id, then execute the deletion. In other words, execute Cursor c = getWritableDatabase().query(), read the id from the cursor, then use it in getWritableDatabase().delete().
Also, add a space after ") FROM", so it becomes ") FROM " in order to avoid a syntax error.
I've got the following cursor set up to fill a dialog box with a users payment history
Cursor PaymentsCursor = db.getReadableDatabase().rawQuery(
"SELECT _id, Date, Payment FROM tblPaymentHistory WHERE DebtName = '"
+ debtname + "'" + "ORDER BY _id ASC", null);
SimpleCursorAdapter HistoryAdapter = new SimpleCursorAdapter(this,
R.layout.paymenthistoryrow, PaymentsCursor, from, to);
The problem is though that if there is more than one type of debt, and payments are made to each debt out of order, when the payment history returns it's results, it returns as out-of-order row numbers, for example 1,2,6,7,9,12,etc. I know it's pulling the _id (unique key) from the database, but is there a way to re-base or change the row number in the query, so that each result returns as "1,2,3,4,5,etc" regardless of original ID?
I thought that the ORDER BY _id or even ORDER BY Date ASC would fix this but it didn't.
My rows in the database look something like this:
1, TEST, 4/13/2012, 250
2, TEST, 4/13/2012, 300
3, TEST, 4/14/2012, 222
4, TEST2, 4/14/2012, 500
5, TEST, 4/15/2012, 600
When the user clicks history for "TEST", it returns back as 1,2,3,5... and if they pull up history for "TEST2", it shows as "4", I'm trying to get it so TEST shows "1,2,3,4" and TEST2 shows "1"
Damn I can't answer my own answer, but here's what I ended up doing:
Thanks guys. I found an alternate option that modified the view, so as not having to touch the SqLite db. heres the link that i foundModifying SimpleCursorAdapter's data
And here is the result:
PaymentsCursor = db.getReadableDatabase().rawQuery(
" SELECT _id, Date, Payment FROM tblPaymentHistory WHERE DebtName = '"
+ debtname + "'" + "ORDER BY _id ASC", null);
String[] from = new String[] { DbAdapter.KEY_HISTORY_ID,
DbAdapter.HISTORY_DATE, DbAdapter.HISTORY_PAYMENT };
int[] to = new int[] { R.id.PAYMENTNO, R.id.PAYMENTDATE,
R.id.PAYMENTAMOUNT };
SimpleCursorAdapter HistoryAdapter = new SimpleCursorAdapter(this,
R.layout.paymenthistoryrow, PaymentsCursor, from, to);
HistoryAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int column) {
if (column == 0) { // let's suppose that the column 0 is the
// date
TextView tv = (TextView) view;
String rownum = String.valueOf(cursor.getPosition() + 1);
// here you use SimpleDateFormat to bla blah blah
tv.setText(rownum);
return true;
}
return false;
}
});
paymenthistory.setAdapter(HistoryAdapter);
It may not be the most glamourous way, but now each time the window comes up with the history, it's using the row number (plus one) to indicate which # it is.
Thanks all!
Here is one way to get the "re-based" ids. In this example, the "new ids" are based on the grade (i.e. the "old ids" in your case):
.headers on
create table foo (name text, grade int);
insert into foo values ('Joe', 45);
insert into foo values ('Anna', 98);
insert into foo values ('Julie', 78);
select name,
grade,
(select count(*) from foo t1 where t1.grade>=t2.grade) as rank
from foo t2;
select name,
grade,
(select count(*) from foo t1 where t1.grade>=t2.grade) as rank
from foo t2
order by rank;
Having saved this as foo.sql, I get this:
[someone#somewhere tmp]$ sqlite3 < foo.sql
name|grade|rank
Joe|45|3
Anna|98|1
Julie|78|2
name|grade|rank
Anna|98|1
Julie|78|2
Joe|45|3
I've played a bit with what #sixfeetsix answered and since that fails to give 1,2,3,4,.. numbering in combination with the WHERE you might need to put in more subqueries (maybe not but, I'm not that good with queries):
SELECT (
SELECT count( * ) + 1
FROM (
SELECT *
FROM tblPaymentHistory
WHERE DebtName = ?
)
AS t1
WHERE t1._id < t2._id
)
AS _id,
Date,
Payment
FROM tblPaymentHistory AS t2
WHERE DebtName = ?
ORDER BY _id;
put in java String and leave the ? in there to get escaped values (injection safe):
Cursor PaymentsCursor = db.getReadableDatabase().rawQuery(
"...WHERE DebtName=? .. WHERE DebtName=? .. ", new String[]{ debtname, debtname });
SimpleCursorAdapter HistoryAdapter = new SimpleCursorAdapter(this,
R.layout.paymenthistoryrow, PaymentsCursor, from, to);
This query worked for me after long research...
Empirical results I derived :
1)You need to define where condition in sub-query also.
2)if ids (t2._id <= t1._id) compared in relation operator will be primary keys then it
will work fine in all cases.
3)Regarding orderby condition you have to decide that according to your choice or need.
SELECT
(SELECT COUNT(*) FROM table t2 WHERE t2._id <= t1._id AND t2.Recipe_id = 2) AS RowNumber,_id,Recipe_id,col2,col3,col4
FROM table t1
WHERE Recipe_id = 2
ORDER BY _id
How it works:-
Say we have a sequence of primary keys 1,2,3,4,5,6 in some table t
Now we create two aliases of it using table t1 and table t2
Now both have same sequence table t1 -> 11,12,13,14,15,16
table t2 -> 11,12,13,14,15,16
now this condition ( WHERE t2._id <= t1._id ) compares first primary key "11" of t2 with
the first primary key "11" of t2 as 11=11 it will return count() that only one row exists, hence we get "1" in row number..
*** remember for every row in Outer query the sub-query is executed ***
Hence now outer query is at row second having primary key "12"
now it will again compare the ( WHERE t2._id <= t1._id ) this time again t2._id contains "11" while t1._id contains "12"..
Quiet clear it will return that TWO rows are there which are having ids <= 12 that is 11 and 12
this way it will generate the desired sequence.........
This is a simple trick to generate the sequence.. not simple actually in one look but really simple when you get into depth of it..
I am not expert but this is what i understood..
Hope the explanation helps...
As there are various solutions or same solutions available on net but no explanation..
:)
I have an issue with SQLite on android. Right now, I'm pulling a JSON object from a server, parsing it, and putting each sub-object in a Table with things such as the Name, Row_ID, unique ID, etc. using this code:
public void fillTable(Object[] detailedList){
for(int i=0;i<detailedList.length;++i){
Log.w("MyApp", "Creating Entry: " + Integer.toString(i));
String[] article = (String[]) detailedList[i];
createEntry(article[0], article[1], article[2], article[3], article[4], article[5]);
}
}
createEntry does what it sounds like. It takes 6 strings, and uses cv.put to make an entry. No problems.
When I try to order them however, via:
public String[] getAllTitles(int m){
Log.w("MyApp", "getTitle1");
String[] columns = new String[]{KEY_ROWID, KEY_URLID, KEY_URL, KEY_TITLE, KEY_TIME, KEY_TAGS, KEY_STATE};
Log.w("MyApp", "getTitle2");
Cursor c = ourDatabase.query(DATABASE_TABLENAME, columns, null, null, null, null, KEY_TIME);
Log.w("MyApp", "getTitle3");
String title[] = new String[m];
Log.w("MyApp", "getTitle4");
int i = 0;
int rowTitle = c.getColumnIndex(KEY_TITLE);
Log.w("MyApp", "getTitle5");
for(c.moveToFirst();i<m;c.moveToNext()){
title[i++] = c.getString(rowTitle);
Log.w("MyApp", "getTitle " + Integer.toString(i));
}
return title;
}
Each entry actually has many duplicates. I'm assuming as many duplicates as times I have synced. Is there any way to manually call the onUpgrade method, which drops the table and creates a new one, or a better way to clear out duplicates?
Secondary question, is there any way to order by reverse? I'm ordering by time now, and the oldest added entries are first (smallest number). Is there a reverse to that?
If you don't want duplicates in one column then create that column with the UNIQUE keyword. Your database will then check that you don't insert duplicates and you can even specify what should happen in that case. I guess this would be good for you:
CREATE TABLE mytable (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
theone TEXT UNIQUE ON CONFLICT REPLACE
)
If you insert something into that table that already exists it will delete the row that already has that item and inserts your new row then. That also means that the replaced row gets a new _id (because _id is set to automatically grow - you must not insert that id yourself or it will not work)
Your second question: you can specify the direction of the order of if you append ASC (ascending) or DESC (descending). You want DESC probably.
Cursor c = ourDatabase.query(DATABASE_TABLENAME, columns, null, null, null, null, KEY_TIME + " DESC");
I am upgrading my database to add another column. What I am trying to do is (after the column is added in onUpgrade) this method is called from the main activity for each table (3 were upgraded). The method is supposed to replace all of the blanks in the new column with "1".
The code runs fine, stepping through, boolean test is true every time but when I open the table to view the data, the entire column is blank. The weird part is, my rowId numbers are incrementing every time. It starts out with 3 rows with rowIds of 1,2,3 respectively. After my code runs once, they now have rowIds of 4,5,6 respectively.
Can anyone help me out? KEY_ROWID is just my auto rowId number. KEY_MODE is just "mode" for column title. If I run through debugging it, the three rows I have show up in the code (it runs through the while loop 3 times).
public void checkBlanks(String table) {
Cursor cursor = mDb.query(table, new String[] {KEY_ROWID, KEY_MODE}, null, null, null, null, null);
while (cursor.moveToNext()) {
int modeCol = cursor.getColumnIndexOrThrow(KEY_MODE);
if (cursor.isNull(modeCol)) {
int rowId = cursor.getInt(cursor.getColumnIndexOrThrow(KEY_ROWID));
ContentValues args = new ContentValues();
args.put(KEY_MODE, 1); // replace the blank space with a "1"
boolean test = mDb.update(table, args, KEY_ROWID + "=" + rowId, null) > 0;
}
}
cursor.close();
}
Instead of manually looping through the rows, why don't you just leverage the power of SQL and update ALL of the rows in one call? E.g.
mDb.execSQL("UPDATE " + table + " SET " + KEY_MODE + " = 1;");
Since it's so simple, you can do this right in your onUpgrade() method.
You could have done that much easier:
1.) During onUpgrade(): "add column newcolumn default 1". This would add a new column with all newcolumns containing 1.
2.) onUpgrade() is already run: update table set newcolumn=1: Without a WHERE clause the whole table is affected.
There's not need to walk thru all rows.
What you want to do requires an SELECT...FOR UPDATE OF/UPDATE...WHERE CURRENT OF. I didn't do that with SQLite, so I don't know if this is supported.
In your situation (onUpgrade is already run) use 2.)