SQLite Database Error(Glitch in bridge between two tables) - android

I have two tables as USER_TABLE where i have all the user details as username,firstname etc and similarly i have another table named RESUME_TABLE where i have aim,degree,university etc.
The columns common to both are KEY_USERID(created dynamically) and i use this id to match the two tables.
Question :
I register a user and his details gets stored in the USER_TABLE and say his KEY_USERID automatically generated(primary key) is 1 and then i create a resume for him in the RESUME_TABLE using the same KEY_USERID 1. I pass on the KEY_USERID obtained from the first table as a argument while inserting and retrieving the resume contents from the RESUME_TABLE.
Say, i do this again for another user thereby generating KEY_USERID 2 but i fill only the
USER_TABLE and the RESUME_TABLE IS EMPTY.
When i repeat this again filling both the tables, then the RESUME_TABLE is not able to fetch the data cause the second user did not fill the details in RESUME_TABLE and that acts as a glitch while retrieving the KEY_USERID for the 3rd user.
How do i rectify this ?
CODE :
DBAdapter.java :
public String get_user_id(String name) {
String[] column = new String[]{KEY_USERID};
String where = KEY_USERNAME + "=?";
Cursor c = mDB.query(DATABASE_TABLE, column, where,new String[]{""+ name + ""}, null, null,null);
String user_id = "";
int iuser_id = c.getColumnIndex(KEY_USERID);
if (c != null)
c.moveToFirst();
user_id = user_id + c.getString(iuser_id) + "\t";
c.close();
return user_id;
}
ViewResume.java
if(get_from_create!=null)
{
if(get_from_create.hasExtra("UserId_Create"))
{
String user_id_create = get_from_create.getExtras().getString("UserId_Create"); // This is where i get the user id from the first table which is created dynamically.
dbAdapter.open();
Log.i("In_View Resume_from_create",user_id_create);
objective.setText(dbAdapter.getObjective(user_id_create));
degree.setText(dbAdapter.getDegree(user_id_create));
passed_out.setText(dbAdapter.getPassedOut(user_id_create));
university.setText(dbAdapter.getUniversity(user_id_create));
field.setText(dbAdapter.getField(user_id_create));
years_of_experience.setText(dbAdapter.getyears_of_experience(user_id_create));
areas_of_interest.setText(dbAdapter.getareas_of_interest(user_id_create));
dbAdapter.close();
}
NOTE:
KEY_USERID is auto incremented only for USER_TABLE and not for RESUME_TABLE.

You can't have two automatic keys referring same elements on different tables.
You should only have automatic primary key in USER_TABLE. KEY_USERID can not be automatic, it must alwys be matched with USER_TABLE.KEY_USERID - effectively, a foreign key.
Furthermore, to understand why you are getting wrong id in code, you must post get_from_create function.

Related

How to fetch user details from a SQLite database having two tables?

I am trying to fetch the user details in my Navigation drawer. I have two different tables, one has username and course already stored and the other has id and email of the user taken during registration. I want to display the user name, id, course and email. I do have a user class with id, email and password for registering but not name and course. How should i fetch data from this class?
The below method is for fetching the username from table having same id. I want to display this on navigation drawer. How should i call the function and display the result?
public String getUserNameFromSID(String sid) {
String rv = "";
String whereclause = KEY_SID + "=?";
String[] whereargs = new String[]{sid};
SQLiteDatabase db = this.getReadableDatabase();
Cursor csr = db.query(TABLE_USERINFO,null,whereclause,whereargs,null,null,null);
if (csr.moveToFirst()) {
rv = csr.getString(csr.getColumnIndex(KEY_NAME));
}
csr.close();
return rv;
You can use a JOIN, (I believe from previous questions that SID is in both tables so this is what you would use as the basis for the JOIN).
So you could use something like SELECT * FROM userinfo JOIN userreg ON userinfo.sid = userreg.sid
Note that just using the column name sid would be ambiguous and hence the need to prefix the column with the table name. As it stands you will have to sid columns in the result (this shouldn't be an issue as they will both be the same value but not specifying an alias using AS cab be problematic for columns with the same name).
To utilise the query convenience method the JOIN needs to be part of the 1st (table) parameter.
So the above would be coded as :-
public String getUserNameFromSID(String sid) {
String rv = "";
String joinclause = " JOIN " + TABLE_USERREG + " ON " + TABLE_USERINFO + "." + KEY_SID + " = " + TABLE_USERREG + "." + KEY_SID; //<<<<<<<<<< ADDED
String whereclause = KEY_SID + "=?";
String[] whereargs = new String[]{sid};
SQLiteDatabase db = this.getReadableDatabase();
Cursor csr = db.query(TABLE_USERINFO + joinclause,null,whereclause,whereargs,null,null,null); //<<<<<<<<<< CHANGED
if (csr.moveToFirst()) {
rv = csr.getString(csr.getColumnIndex(KEY_NAME));
}
csr.close();
return rv;
}
Note the above is in-principle code as such
variable names and identifiers may not reflect the actual code and thus may need to be changed.
the code has not been run or tested and may therefore contain some minor errors.
The above only returns the username, you cannot return multiple distinct values, you would therefore need to return an object e.g. a User object to encompass the multiple values. You would get the values to set the object in a similar way as rv is set above.
Additional re comment :-
The thing is, that i would need to pass the Sid parameter to the
function which i can only do in the register/login page. How then can
i pass this value to the navigation page?
Instead of pages the term used for Android is Activities. You can pass data from one activity to another called/started/invoked activity using Intent Extras after instantiating an Intent in preparation for the call. That is if the activity is to be directly called from the parent activity (registration activity in your case).
If for example the registration activity were called from an initial activity and returns from that activity then you still use an intent to return the values, but the registration activity should be called/started/invoked using startActivityForResult. The activity starting the registration activity should override the onActivityResult method. The registration activity sets intent extras and uses setResult to indicate the result code and then intent to be returned.
There's plenty of examples on Stack overflow e.g. Sending data back to the Main Activity in Android

How to delete the full row of a SQLite database by entering only a string value of a particular column?

I am using this method to get query for this string:
public void deletedata(){
p=srt.split(",");
DatabaseHandler dba=new DatabaseHandler(this);
for(String s:p) {
dba.removeSingleproduct(s);
}
Database method is :
public boolean removeSingleproduct(String name) {
SQLiteDatabase db = this.getWritableDatabase();
return db.delete(tablename, productinserted + "=" + name, null) > 0;
}
I want to delete only one row by calling database as product inserted can have two same value.
Please help guys.
Since you're deleting with a selectedValue String,
add a single quote before and after the name
return db.delete(tablename, productinserted + " = '" + name + "'", null) > 0;
Or you can simplify your code.
public int removeSingleproduct(String name) {
return getWritableDatabase().delete(tablename, productinserted + " = ?", new String[] { name });
}
Return int - the number of rows affected if a whereClause is passed in, 0 otherwise. To remove all rows and get a count pass "1" as the whereClause.
The following will use the name to locate all rows with the provided name but only delete the first according to it's rowid (unless WITHOUT ROWID has been specified [very likely not]).
public boolean removeSingleproduct(String name) {
boolean rv = false;
SQLiteDatabase db = this.getWritableDatabase();
Cursor csr = db.query(tablename,new String[]{"rowid AS dltid"},productinserted + "=?",new String[]{name},null,null,null);
if(csr.moveToFirst()) {
rv = db.delete(tablename,"rowid=?",new String[]{Long.toString(csr.getLong(csr.getColumnIndex("dltid")))}) > 0;
}
csr.close();
return rv;
}
If you wanted to ensure that a row was only deleted if multiple rows with the same productinserted name existed, then you could simply change
if(csr.moveToFirst()) { ........
to
if(csr.moveToFirst() && csr.getCount() > 1) { .......
Note! csr.moveToLast() could be used instead of csr.moveToFirst() it would probably then delete the newest addition rather than probably deleting the oldest addition.
If you think
but I haven't defined a column called rowid
then :-
Except for WITHOUT ROWID tables, all rows within SQLite tables have a
64-bit signed integer key that uniquely identifies the row within its
table. This integer is usually called the "rowid". The rowid value can
be accessed using one of the special case-independent names "rowid",
"oid", or "rowid" in place of a column name. If a table contains a
user defined column named "rowid", "oid" or "rowid", then that name
always refers the explicitly declared column and cannot be used to
retrieve the integer rowid value.
SQL As Understood By SQLite - ROWIDs and the INTEGER PRIMARY KEY

Contacts are saved multiple times in Database

Hi guys i have code to add data into Database and that code is called on oncreate. but every time fragment is created its saves data into database again and again. I dont want that. how can i prevent that here is my code to get data and save it to database.
public void getcontacts(){
Cursor phones;
phones = getActivity().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null, null);
while (phones.moveToNext()){
String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
DataBaseOperations hell = new DataBaseOperations(getActivity());
SQLiteDatabase db = hell.getWritableDatabase();
hell.SaveContacts(name, phoneNumber, db);
}
phones.close();
}
here is code for saving in database.
public void SaveContacts(String Name,String phone,SQLiteDatabase db ){
ContentValues sv = new ContentValues();
sv.put(mDatabase.Tableinfo.Contacts_name, Name);
sv.put(mDatabase.Tableinfo.Contacts_phone, phone);
db.insert(mDatabase.Tableinfo.contacts, null, sv);
}
EDIT- Here is what i have done so far but how do i set a constraint on which it will conflict.I have added an auto increment primary key as unique key but how will they conflict?? but still its saving values again in database.where do i set a primery key as constraint?????
public void SaveContacts(String Name,String phone,SQLiteDatabase db ){
ContentValues sv = new ContentValues();
sv.put(mDatabase.Tableinfo.Contacts_name, Name);
sv.put(mDatabase.Tableinfo.Contacts_phone, phone);
db.insertWithOnConflict(mDatabase.Tableinfo.contacts, null, sv, SQLiteDatabase.CONFLICT_REPLACE);
EDIT- 2 - how would i make unique columns in this kind of query???
private static final String Contacts_Table = "CREATE TABLE "+
mDatabase.Tableinfo.contacts +"("
+mDatabase.Tableinfo.Contacts_name+" TEXT,"
+mDatabase.Tableinfo.Contacts_phone+" TEXT NOT NULL UNIQUE,"
+mDatabase.Tableinfo.isChattris+" TEXT,"
+mDatabase.Tableinfo.status_contact+" TEXT,"
+mDatabase.Tableinfo.Contact_pic+" BLOB"+")";
just Added NOT NULL UNIQUE.
This is the constraint.
Keep the status of the contacts saving in unrelated storage, e.g SharedPreferences. You don't want to rely on any lifecycle triggers, not even Application.onCreate, since that will happen again and again when the app is launched.
Try to update existing entries instead of adding new ones. Decide what the database key should be, and then you can use insertWithOnConflict with the CONFLICT_REPLACE flag to insert if not already in the table or update if it is.
EDIT: to define the constraints so the conflict behavior will trigger, change your CREATE TABLE statement. e.g this will cause duplicate (name,phone) pairs to trigger it. You may want to use some kind of contact ID instead though.
CREATE TABLE mytable (
name TEXT,
phone TEXT,
UNIQUE(name, phone)
)

Android SqLite no such column _id exception

Don't immediately flag me for a duplicate question. My issue is different because I have a correctly formatted SQL query.
public static final String TABLE_NAME = "log";
public static final String COLUMN_ID = "_id";
public static final String LOG_TEXT = "logtext";
private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " (" +
COLUMN_ID + " integer primary key autoincrement, " +
LOG_TEXT + " TEXT not null);";
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TABLE_CREATE);
}
and I query here
String[] columns = {LOG_TEXT,COLUMN_ID};
Cursor cursor = helper.getReadableDatabase().query(TABLE_NAME, columns, null, null, null, null, COLUMN_ID + " desc");
and I catch this the exception generated containing the sql query.
catch(Exception e){
Log.D("sql Exception",e.getMessage());}
and it returns
no such column: _id: , while compiling: SELECT logtext, _id FROM log ORDER BY _id desc
I'm familar with Oracle SQL and relational databases in general. Is it my ORDER BY clause? I was certain you can ALWAYS use order by. It doesn't have the same behavior as GROUP BY.
Any ideas on why the exception?
Incase anyone wants to see i'm updating with my ArrayAdaptor statements. I'm using the cursor in a listview
String[] data = query();
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, data);
listView.setAdapter(adapter);}
Rewrite
Whenever you change the schema in TABLE_CREATE you must inform you app of these changes, they will not happen automatically when you change TABLE_CREATE. The easiest way to do this is to increment your database_version in your extended SQLiteOpenHelper class. You discovered you can also uninstall / reinstall the app, for the same results. If you are savvy with SQL you could ALTER the table. But whatever the method you must make sure that you app makes the schema changes before trying to access the new columns...
Also for SQLite:
_id integer primary key
is synonymous with:
_id integer primary key autoincrement not null
And queries use descending as the default order, so ORDER BY _id is the same as ORDER BY _id DESC.
Had the same problem, meaning it should have worked but didn't (had some typos in the create command that I fixed but that still didn't help). A colleague then told me to try clearing the data (just at AppInfo and then "Clear Data") which solved my problem, apparently the old database (that didn't work) was still there and had to be cleared out first.
I just put this answer here in case anybody else like me (android beginner) stumbles across this problem, because I went through dozens of stackoverflow threads with this problem but not one offered this possibility/solution and it bothered me for quite some time.
Did you add the definition of the _id column to your create statement later on, i.e. after the code had already been run once? Databases are persisted files, so if you modify the table structure in your code you need to make sure you clear your application's data so the database file can ge re-created with the correct table/column data.

Android database strangeness listing columns

I am getting inconsistent results between two methods of reading the columns in an Android SQLite database.
First, this is part of a database upgrade routine as per the accepted answer here: Upgrade SQLite database from one version to another?
The technique involves moving the current table away with a temporary name, creating a new table with the new schema, and then copying relevant data from the old table into the new one before deleting the old temporary table.
The particular problem I have is when I remove a column from the schema. So, a particular column exists in the old version of the table, but not the new one.
That answer suggests using a method like this to list the columns in the table:
/**
* Returns a list of the table's column names.
*/
private List<String> getColumns(SQLiteDatabase db, final String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("SELECT * FROM " + tableName + " LIMIT 1", null);
if (c != null) {
ar = new ArrayList<String>(Arrays.asList(c.getColumnNames()));
}
} finally {
if (c != null)
c.close();
}
return ar;
}
That works fine on the old table, before I move it away with a temporary name and replace it. When I run the same query again later, on the newly-created empty table, it still lists the old table schema with the name of the column which no longer exists. It looks as if it's reusing stale cached results for that query.
If I read the columns a different way, using this instead, then it returns the new column list as expected:
private void listColumns(SQLiteDatabase db, final String tableName) {
final String query = "PRAGMA table_info(" + tableName + ");";
Cursor c = db.rawQuery(query, null);
while (c.moveToNext()) {
Log.v("MyApp", "Column: " + c.getString(1));
}
c.close();
}
The complete sequence is:
final String tempTableName = "temp_" + tableName;
table.addToDb(db); // ensure it exists to start with
// get column names of existing table
final List<String> columns = getColumns(db, tableName);
// backup table
db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tempTableName);
// create new table
table.addToDb(db);
// delete old columns which aren't in the new schema
columns.retainAll(getColumns(db, tableName));
// restore data from old into new table
String columnList = TextUtils.join(",", columns);
db.execSQL(String.format("INSERT INTO %s (%s) SELECT %s from %s", tableName, columnList, columnList,
tempTableName));
// remove backup
db.execSQL(DROP_TABLE + tempTableName);
What's the reason for the different results?
I assume you have done something similar to this:
ALTER TABLE "main"."mytable" RENAME TO "newtable";
CREATE TABLE "main"."mytable" ("key1" text PRIMARY KEY,"key2" text,"key3" text);
INSERT INTO "main"."mytable" SELECT "key1","key2","key3" FROM "main"."newtable";
DROP TABLE "main"."newtable";
If you have, please share the equivalent code, just to rule out any errors with this part.
I never got to the bottom of this. I just ended up using the second method I mentioned, which doesn't exhibit the problem.

Categories

Resources