deleting one duplicate row in sqlite - android

I wish to just delete one duplicate row in here (For example, Jim 21)
SQLiteDatabase myDataBase=this.openOrCreateDatabase("Users",MODE_PRIVATE,null);
myDataBase.execSQL("CREATE TABLE IF NOT EXISTS users (name VARCHAR,age INT(3))");
myDataBase.execSQL("INSERT INTO users(name,age) VALUES ('Rob', 34)");
myDataBase.execSQL("INSERT INTO users(name,age) VALUES ('Nat', 22)");
myDataBase.execSQL("INSERT INTO users(name,age) VALUES ('Jim', 21)");
myDataBase.execSQL("DELETE FROM users WHERE name='Jim'");
Cursor c=myDataBase.rawQuery(" SELECT * FROM users", null);
int nameIndex=c.getColumnIndex("name");
int ageIndex=c.getColumnIndex("age");
c.moveToFirst();
while (c!=null){
Log.i("name",c.getString(nameIndex));
Log.i("age",Integer.toString(c.getInt(ageIndex)));
c.moveToNext();
}
I have tried this
myDataBase.execSQL("DELETE FROM users WHERE name='Jim' LIMIT 1");
But it is throwing a syntax error. I know LIMIT is not syntactically allowed in android. So how do I just delete one record of Jim when there are duplicates?
Thank you.

Limit will not work with Delete query,it's only for Select number of record
Update the query
myDataBase.execSQL("DELETE FROM users WHERE name='Jim'");
you can add more condition for remove specific records
myDataBase.execSQL("DELETE FROM users WHERE name='Jim' AND age=21 ");

There are several ways to achieve this. However, I would suggest to put a unique constraint on your name field.
myDataBase.execSQL("CREATE TABLE IF NOT EXISTS users (name text unique not null, age INT(3))");
Now for creating new entries in your users table, get a function like the following.
public void createUser(List<User> userList) {
if (userList != null && !userList.isEmpty()) {
SQLiteDatabase db = this.openOrCreateDatabase("Users",MODE_PRIVATE,null);
db.beginTransaction();
try {
for (User user : userList) {
ContentValues values = new ContentValues();
values.put("name", user.getName());
values.put("age", user.getAge());
// Replace on conflict with the unique constraint
db.insertWithOnConflict("users", null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
} catch (Exception e) {
e.printStackTrace();
}
db.setTransactionSuccessful();
db.endTransaction();
}
}
In this way, you do not have to delete any duplicate rows in your table as there will be no duplicate rows either.
However, if your implementation needs duplicate rows and then deleting only the first when you are trying to delete based on some condition then you might consider using the sqlite built-in column ROWID. You get all the rows that matches your condition and save the ROWID of them all. Then you delete the row that matches the ROWID you want to delete.
delete from users where ROWID = 9
Here's the developers documentation of using ROWID.

The approach I would take is to create a table where the duplicates are automatically resolved when data is inserted. Make the "name" field a primary key. Here's the CREATE statement:
CREATE TABLE users (name TEXT PRIMARY KEY ON CONFLICT IGNORE,age INTEGER);
"ON CONFLICT IGNORE" will always keep the first "name" record in the database. If you want to always keep the last record inserted, use "ON CONFLICT REPLACE". For example:
INSERT INTO users VALUES ('Jim','21');
INSERT INTO users VALUES ('Jim','23');
INSERT INTO users VALUES ('Jim','43');
If you use "ON CONFLICT IGNORE" Then "SELECT * FROM users" would produce "Jim|21". If you use "ON CONFLICT REPLACE" Then "SELECT * FROM users" would produce "Jim|43".

Related

What is the best way to delete a record of multi tables?

I have a database that has three tables and there is a class_id column in all three tables. i want to when i deleting a class_id , Deleted all records in the tables that have the class_id
I used a way , but I'm not sure this way is standard or no ?
tip : class_id in a table is primary key and in another tables are foreign key
public void DeleteClass(int classId)
{
String query = "class_id = ?";
OpenDatabase();
database.delete(tblName_Class, query , new String[]{String.valueOf(classId)});
database.delete(tblName_Student, query , new String[]{String.valueOf(classId)});
database.delete(tblName_StudentPerformance , query , new String[]{String.valueOf(classId)});
close();
Toast.makeText(context, "deleted !", Toast.LENGTH_SHORT).show();
}
It can depend on the structure of your database. If you have defined the foreign keys with an ON DELETE CASCADE, you would only need to delete from the Class table and it would automatically delete from the other two.
Now if you haven't defined the ON CASCADE DELETE, the way to do it is to delete first from the tables that have the foreign key (In your case Student and StudentPerformance) and then the one with the primary key (Class).

Why does insertWithOnConflict(..., CONFLICT_IGNORE) return -1 (error)?

I have an SQLite table:
CREATE TABLE regions (_id INTEGER PRIMARY KEY, name TEXT, UNIQUE(name));
And some Android code:
Validate.notBlank(region);
ContentValues cv = new ContentValues();
cv.put(Columns.REGION_NAME, region);
long regionId =
db.insertWithOnConflict("regions", null, cv, SQLiteDatabase.CONFLICT_IGNORE);
Validate.isTrue(regionId > -1,
"INSERT ON CONFLICT IGNORE returned -1 for region name '%s'", region);
On duplicate rows insertWithOnConflict() is returning -1, indicating an error, and Validate then throws with:
INSERT ON CONFLICT IGNORE returned -1 for region name 'Overseas'
The SQLite ON CONFLICT documentation (emphasis mine) states:
When an applicable constraint violation occurs, the IGNORE resolution algorithm skips the one row that contains the constraint violation and continues processing subsequent rows of the SQL statement as if nothing went wrong. Other rows before and after the row that contained the constraint violation are inserted or updated normally. No error is returned when the IGNORE conflict resolution algorithm is used.
The Android insertWithOnConflict() documentation states:
Returns
the row ID of the newly inserted row OR the primary key of the existing row if the input param 'conflictAlgorithm' = CONFLICT_IGNORE OR -1 if any error
CONFLICT_REPLACE isn't an option, because replacing rows will change their primary key instead of just returning the existing key:
sqlite> INSERT INTO regions (name) VALUES ("Southern");
sqlite> INSERT INTO regions (name) VALUES ("Overseas");
sqlite> SELECT * FROM regions;
1|Southern
2|Overseas
sqlite> INSERT OR REPLACE INTO regions (name) VALUES ("Overseas");
sqlite> SELECT * FROM regions;
1|Southern
3|Overseas
sqlite> INSERT OR REPLACE INTO regions (name) VALUES ("Overseas");
sqlite> SELECT * FROM regions;
1|Southern
4|Overseas
I think that insertWithOnConflict() should, on duplicate rows, return me the primary key (_id column) of the duplicate row — so I should never receive an error for this insert. Why is insertWithOnConflict() throwing an error? What function do I need to call so that I always get a valid row ID back?
The answer to your question, unfortunately, is that the docs are simply wrong and there is no such functionality.
There is an open bug from 2010 that addresses precisely this issue and even though 80+ people have starred it, there is no official response from the Android team.
The issue is also discussed on SO here.
If your use case is conflict-heavy (i.e. most of the time you expect to find an existing record and want to return that ID) your suggested workaround seems the way to go. If, on the other hand, your use case is such that most of the time you expect for there to be no existing record, then the following workaround might be more appropriate:
try {
insertOrThrow(...)
} catch(SQLException e) {
// Select the required record and get primary key from it
}
Here is a self-contained implementation of this workaround:
public static long insertIgnoringConflict(SQLiteDatabase db,
String table,
String idColumn,
ContentValues values) {
try {
return db.insertOrThrow(table, null, values);
} catch (SQLException e) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ");
sql.append(idColumn);
sql.append(" FROM ");
sql.append(table);
sql.append(" WHERE ");
Object[] bindArgs = new Object[values.size()];
int i = 0;
for (Map.Entry<String, Object> entry: values.valueSet()) {
sql.append((i > 0) ? " AND " : "");
sql.append(entry.getKey());
sql.append(" = ?");
bindArgs[i++] = entry.getValue();
}
SQLiteStatement stmt = db.compileStatement(sql.toString());
for (i = 0; i < bindArgs.length; i++) {
DatabaseUtils.bindObjectToProgram(stmt, i + 1, bindArgs[i]);
}
try {
return stmt.simpleQueryForLong();
} finally {
stmt.close();
}
}
}
While your expectations for the behavior of insertWithOnConflict seems entirely reasonable (you should get the pk for the colliding row), that's just not how it works. What actually happens is that you: attempt the insert, it fails to insert a row but signals no error, the framework counts the number of rows inserted, discovers that the number is 0, and, explicitly, returns -1.
Edited to add:
Btw, this answer is based on the code that, eventually, implements insertWithOnConflict:
int err = executeNonQuery(env, connection, statement);
return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
? sqlite3_last_insert_rowid(connection->db) : -1;
SQLITE_DONE is good status; sqlite3_changes is the number of inserts in the last call, and sqlite3_last_insert_rowid is the rowid for the newly inserted row, if any.
Edited to answer 2nd question:
After re-reading the question, I think that what you are looking for is a method that does this:
inserts a new row into the db, if that is possible
if it cannot insert the row, fails and returns the rowid for the existing row that conflicted (without changing that rowid)
The whole discussion of replace seems like red herring.
The answer to your 2nd question, then, is that there is no such function.
Problem has already been solved, but this may be an option which solved my issue. Just changing the last parameter to CONFLICT_REPLACE.
long regionId =
db.insertWithOnConflict("regions", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
Hope it helps.

How to put non Duplicate values in SQLite & Access?

My Create table Query is:--
String CREATE_LOGIN_TABLE ="CREATE TABLE IF NOT EXIST "+ TABLE_FORWARDMSG +"("+KEY_ID+ " INTEGER PRIMARY KEY AUTOINCREMENT,"+KEY_MSG_BODY+ " TEXT UNIQUE,"+KEY_MSG_ADDRESS+" TEXT UNIQUE,"+KEY_MSG_DATE+" TEXT UNIQUE" +")";
and I am access values From This Code:---
String selectQuery="SELECT * FROM "+TABLE_FORWARDMSG;
SQLiteDatabase db=this.getReadableDatabase();
Cursor cursor=db.rawQuery(selectQuery, null);
if( cursor.moveToFirst()){
for(int i=0;i<cursor.getCount();i++){
map.put("msgBody", cursor.getString(1));
map.put("msgAddress", cursor.getString(2));
map.put("msgDate", cursor.getString(3));
user.add(map);
cursor.moveToNext();
}
}
cursor.close();
db.close();
Now My Problem is that:----
If I remove UNIQUE from the CREATE TABLE Then I get all duplicate values means if I insert same value it create new row, and if I am using UNIQUE in CREATE TABLE, then every time cur.getCount() value is 1. I am new in SQLite. Please tell me whats the problem.
Your code looks like Ok. Maybe you shouldn't make KEY_MSG_DATE like UNIQUE. Try to get more info about what you want to do.
What you have implemented is that in every entry in every column has to be unique within the column but I assume what you want is that the row containing the columns msgBody, msgAddress and msgDate has to be unique within the table.
You can achieve that by placing all columns together in the UNIQUE clause:
UNIQUE(msgBody, msgAddress, msgDate) ON CONFLICT REPLACE

Create a FIFO table with SQlite for Android application

I would like to create a FIFO table in order to save only the most 50 recent infomations by deleting the oldest elements when a new infomation arrives. I can do it by manipulating ID in the table but I don't think it is the best solution. Any idea of doing it well?
Instead of checking for date time, sorting your items, and whatnot, you can just assume that the first row in your table is the last to be inserted.
In your Content Provider's insert(Uri uri, ContentValues cv), before doing your db.insert call, you can first query the number of items on that table using getCount() and delete the first row if count>50. Then proceed with your insert call.
You dont need to play with IDs in order to create a FIFO logic. The best would be to add another column as DATETIME in your table which automatically inserts current time-stamp that will help you to select records in ascending order with respect to this column. Your new column should be something like:
DateAdded DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
Make sure when ever you insert new record, you must do a COUNT check of total records in this table and if necessary delete the oldest record with respect to DateAdded. Moreover, you can make use of LIMIT and/or MAX in your select-query when it comes to delete the oldest record.
Add a datetime type column to your table if it doesn't contain it yet and set it to 'now' on each insert. Then on each insert select all with limit set to 50 sorted by date. Choose the last item and run a delete query to delete everything older than this last item.
is it must to use sqlite? can you use file handling? you can use simple Queue object and save it to file.
Here is what I did for a list of transactions, and it works okay. When inserting a new entry I check if the count is above 50, if so, I just delete the very last entry:
// Adding new transaction
public void addTransaction(Transaction transaction) {
SQLiteDatabase db = this.getWritableDatabase();
if(getTransactionsCount() > 50){
List<Transaction> allTransactions = getAllTransactions();
Transaction oldestTransaction = allTransactions.get(allTransactions.size()-1);
deleteTransaction(oldestTransaction);
}
ContentValues values = new ContentValues();
values.put(KEY_TRANSACTION_UID, transaction.getUID());
values.put(KEY_TRANSACTION_AMOUNT, transaction.getAmount());
values.put(KEY_TRANSACTION_IS_ADD, transaction.getIsAdd());
// Inserting Row
db.insert(TABLE_TRANSACTIONS, null, values);
db.close(); // Closing database connection
}
And getAllTransactions() returns the list in descending order (based on the id primary key):
// Getting All Transactions
public List<Transaction> getAllTransactions() {
List<Transaction> transactionList = new ArrayList<Transaction>();
// Select All Query
String selectQuery = "SELECT * FROM " + TABLE_TRANSACTIONS + " ORDER BY " + KEY_TRANSACTION_ID + " DESC";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Transaction transaction = new Transaction();
transaction.setID(Integer.parseInt(cursor.getString(0)));
transaction.setUID(cursor.getString(1));
transaction.setAmount(cursor.getString(2));
transaction.setIsAdd(cursor.getString(3));
// Adding contact to list
transactionList.add(transaction);
} while (cursor.moveToNext());
}
// return contact list
return transactionList;
}

SQLiteDatabase.insert() returns incorrect rowid for virtual tables

When I insert a row into a full-text search database declared like:
CREATE VIRTUAL TABLE foo USING fts3 (bar);
the SQLiteDatabase.insert() method returns an incorrect rowid. If the table is declared like:
CREATE TABLE foo (bar VARCHAR(10));
it returns the correct rowid value. The problem is when I query the database soon after the insert using the rowid returned from the method, the returned Cursor has no records. It works correctly for the first insert into the database only. For subsequent inserts, an incorrect rowid is returned.
Is there anything I need to do to get the correct rowid from the SQLiteDatabase.insert() method?
I'm using Android SDK version 2.1update1.
Thanks,
Dan
Update:
I ended up using a hack to get the last row id using the following code:
private int getLastRowId(SQLiteDatabase db, String table) {
Cursor cursor = null;
try {
cursor = db
.rawQuery(String.format(Locale.US, "SELECT MAX(rowid) FROM %s", table), null);
if (cursor.moveToFirst()) {
return cursor.getInt(0);
} else {
return 0;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
In my case, it's safe because only a single user has access to the app. For service apps, this may not work depending on how it is implemented/used.
I believe we have this problem because when performing an insert in fts3 tables, more than one row is inserted. A row is inserted in the subject table and in the fts3 management tables as well.
From SQLite Full-Text Search:
Your table must contain at least 1
TEXT field.
PS: +1: I didn't know about Virtual Tables. Thanks.

Categories

Resources