How to find unique rows from table sqlite android? - android

How can i get the unique rows from a table in sqlite database android

Suppose, I have 5 rows in a table(user_table) like below
id
name
dcode
tno
dname
1
satheesh
A01
2
ABC
2
mahesh
A01
2
ABC
3
suresh
A02
1
ABC
4
naresh
A02
1
ABC
5
rajesh
A03
3
ABC
To get unique rows with dcode
#Query("select distinct dCode, tno, dname from user_table")
suspend fun getUniqeList(): List<UserDataModel>?
and create UserDataModel class like below
data class UserDataModel (
var dCode: String? = null,
var tno: String? = null,
var dname: String? = null,
)
it will print only three row like below
dcode
tno
dname
A01
2
ABC
A02
1
ABC
A03
3
ABC

Related

Room Library - relationship between tables having auto generated keys

The examples I came across were forming relations without primary key, my database schema is as follow.
I have primary key in both tables, and I'm attempting to create a DAO method that will join both user and department tables to display a record.
Question, how to create a DAO method to access the record joined by the department information. I prefer to do without creating more data classes if possible. for instance using Map<User, Department>. If there's none then I will accept other solutions.
User Table
+----+----------+----------+---------------+
| id | username | name | department_id |
+----+----------+----------+---------------+
| 1 | johndoe | John Doe | 3 |
| 2 | janedoe | Jane Doe | 4 |
+----+----------+----------+---------------+
User data class
#Entity(tableName = "user")
data class User(
#PrimaryKey (autoGenerate = true) var id: Long,
var username: String,
var name: String,
var department_id: Long)
Department Table
+----+----------------+
| id | name |
+----+----------------+
| 1 | Sales |
| 2 | Account |
| 3 | Human Resource |
| 4 | Marketing |
+----+----------------+
Department data class
#Entity(tableName = "department")
data class Department(
#PrimaryKey(autoGenerate = true) var id: Long,
var name: String)
My attempt at DAO
#Dao
interface UserDAO {
#Query("SELECT user.*, department.name AS 'department_name' FROM user " +
"INNER JOIN department ON user.department_id = department.id " +
"WHERE user.id = :id")
fun findById(id: Long): Map<User, Department>
}
Edit
Will it work if Department is composed inside User? If that's the case then how the query inside DAO would look like?
#Entity(tableName = "user")
data class User(
#PrimaryKey (autoGenerate = true) var id: Long,
var username: String,
var name: String,
#Embedded var department: Department)
data class Department(
#PrimaryKey(autoGenerate = true)
var id: Long,
var name: String? = null): Parcelable
DAO attempt
#Query("SELECT * FROM user " +
"INNER JOIN department d ON department.id = d.id " +
"WHERE id = :id")
fun findById(id: Long): Map<User, Department>
Your code works as it is BUT due to ambiguous column names (id and name) values are not as expected.
Here's the result (noting that User id's are 500 and 501 instead of 1 and 2 to better highlight the issue) when debugging, with a breakpoint at a suitable place and when using:-
findById(500)
then the debugger shows :-
That is that although John's department id is 3, it is instead 500 in the Department itself, likewise the name of the department is John Doe not the expected *Human Resource.
This is because Room doesn't know which id is for what and likewise name.
The fix is to use unique column names. e.g. with:-
#Entity(tableName = "department")
data class Department(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "dept_id")
var id: Long,
#ColumnInfo(name = "dept_name")
var name: String
)
i.e. the columns in the table, but not in the Department have been changed. You could just change the name of the fields to be unique names.
Obviously if the column names are changed then the SQL has to be changed accordingly, so :-
#Query("SELECT * FROM user " +
"INNER JOIN department ON department_Id = dept_id " +
"WHERE id = :id")
fun findById(id: Long): Map<User, Department>
as the column names now have no ambiguities then there is no need to use table names to differentiate (you still can. Without the table banes the SQL can be more concise.
Now Debug shows:-
Additional re Edit
Will it work if Department is composed inside User?
If you use #Embedded within an #Entity annotated class that is included in the entities parameter of the #Database annotated class then the resultant table has columns as per the embedded class.
If the embedded class was previously a table then in respect of the relationship that doesn't exist and that previous table may well be defunct.
There is an issue with the embedded Department in that you will have 2 columns named id
From a database perspective, if what was a 1 to many relationship then the same data (e.g. Human Resource) will be repeated and is contrary to normalisation.
In regard to the query
#Query("SELECT * FROM user " +
"INNER JOIN department d ON department.id = d.id " +
"WHERE id = :id")
fun findById(id: Long): Map<User, Department>
Then this will not work as there will be no department table (as according to your code the Department class is not annotated with #Entity).
Room supports this since version 2.4 as described in the official documentation. Similar issue to yours appear to be solved also in this answer https://stackoverflow.com/a/72990697/5473567
In your concrete example you are forgetting, that the relation is 1:N, therefore your return type in the DAO function should look like this Map<User, List<Department>>.
Also the department.name AS 'department_name' in your SELECT is wrong. The field name in the data class Department is called name, therefore also in Database table department this field will be called name. If you would like to change its name to the department_name as you use in your SELECT, you have to use annotation #ColumnInfo as described in here. The data class will then look like this:
#Entity(tableName = "department")
data class Department(
#PrimaryKey(autoGenerate = true) var id: Long,
#ColumnInfo(name = "department_name") var name: String
)
Last tip, if you are pulling full data from the database, there is enough to use SELECT * FROM..., Room will resolve it for you.
Finally the whole DAO interface may look like this:
#Dao
interface UserDAO {
#Query("SELECT * FROM user " +
"INNER JOIN department ON user.department_id = department.id " +
"WHERE user.id = :id")
fun findById(id: Long): Map<User, Department>
}
Solved. Based on the answers, here's my solution without using additional data classes while also keeping the column names intact, for instance using id column in each table without any prefix (preferable). I had to use user.id in the WHERE clause to avoid ambiguity
User
#Entity(tableName = "user")
data class User(
#PrimaryKey (autoGenerate = true)
var id: Long,
var username: String,
var name: String,
var department_id: Long)
Department
#Entity(tableName = "department")
data class Department(
#PrimaryKey(autoGenerate = true)
var id: Long,
var name: String)
DAO Query
#Query("SELECT * FROM user " +
"INNER JOIN department ON user.department_id = department.id " + // fixed from user.id = department.id
"WHERE user.id = :id")
fun find(id: Long): Map<User, Department>

How to add to previously pre-populated Room database?

Previously, I pre-populated some data and added it to the Room db. But I have some new data that I need to add. As a way to add these, I can do it by requesting the dao directly, but the application will do this every time it is opened. This will have brought an unnecessary burden to the application. So is there a better way than the code below? I'm asking if there is a better way than this.
private fun addNewWord(){
val newWord1 = Word(1, "John", "Weight")
val newWord2 = Word(2, "Alex", "So"
wordViewModel.addWord(newWord1, newWord2)
}
I found a solution like the code I wrote above, but I don't think it's correct enough. I'm looking for a better way. Is this a best-practice?
The are numerous better (as far as This will have brought an unnecessary burden to the application goes), assuming that the unnecessary burden is the overhead associated with the attempt to insert records whenever the App is run (more correctly whenever the database is opened).
The simplest solution, but one that is not really better (it may even be what you have tried) would be to IGNORE duplicates (if you aren't already ignoring them). This involves using INSERT OR IGNORE .... where a column or columns or a combination of columns has a UNQIUE index.
All Room tables (other than for FTS (Full Text Search)) MUST have a Primary Key. A Primary Key is implicitly UNIQUE. So if INSERT OR IGNORE .... is used then the UNIQUE CONFLICT is ignored (the row is not inserted and the conflict which would result in an exception is ignored).
To specify INSERT OR IGNORE for a convenience #Insert then you can specify the onConflict value of the #Insert annotation. e.g.
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(word: Word): Long
the convenience #Insert returns the rowid of the inserted row or -1 if the row was not inserted due to it being ignored, this could be checked to see if a row was inserted.
a normally hidden column that exists for ALL Room tables (as it exists for any SQLite table that is not a virtual table or a table that is defined as an WITHOUT ROWID table).
You can also specify unique indexes on a column or a combination of columns via the indicies parameter of the #Entity annotation e.g.:-
#Entity(
indices = [
/* A composite UNIQUE index on word1 combined with word2 */
Index(value = ["word1","word2"], unique = true)
/* example
* if a row exists that has A as word1 and B as word2 then (index value can be considered as AB):-
* inserting a new row with B and A would be inserted (index value BA)
* inserting a new row with B and B (or A and A) would be inserted (index value BB or AA)
* inserting a row with B and A would result in a UNIQUE CONFLICT
* inserting a row with BA and nothing (aka null) would be inserted (assuming NOT NULL was not coded or implied for column word2)
*/
/*
An Index on a single column (word1 so the value in the word1 column cannot be duplicated)
NOTE the above would not apply if this index was also included
In this case then word1 =A and word2 = A above (third insert (or A and A)) would be considered a duplicate
*/
, Index(value = ["word1"], unique = true)
])
data class Word(
#PrimaryKey
var id: Long?=null,
var word1: String,
var word2: String
)
However, this simple solution would still run and try to insert the new data whenever the App is run.
A better solution without the "unnecessary burden"
If the goal is to only apply new data once then there would need to be a method to see if the data has already been applied, perhaps via a Migration (aka a new version).
The Migration would only run once as the user_version, which is part of the databases file header is checked/updated by Room.
The migration would also be run if the App is installed after the new database version has been specified.
Working Demo
Perhaps consider the following Migration based Working Demo based upon what you data appears to be:-
The Room database code:-
#Entity(
indices = [
/* A composite UNIQUE index on word1 combined with word2 */
Index(value = ["word1","word2"], unique = true)
/* example
* if a row exists that has A as word1 and B as word2 then (index value can be considered as AB):-
* inserting a new row with B and A would be inserted (index value BA)
* inserting a new row with B and B (or A and A) would be inserted (index value BB or AA)
* inserting a row with B and A would result in a UNIQUE CONFLICT
* inserting a row with BA and nothing (aka null) would be inserted (assuming NOT NULL was not coded or implied for column word2)
*/
])
data class Word(
#PrimaryKey
var id: Long?=null,
var word1: String,
var word2: String
)
#Dao
interface TheDAOs {
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(word: Word): Long
#Query("SELECT * FROM word")
fun getAllWords(): List<Word>
}
const val DATABASE_NAME = "the_database.db"
const val ASSET_NAME = DATABASE_NAME
#Database(entities = [Word::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() {
abstract fun getTheDAOs(): TheDAOs
companion object {
private var instance: TheDatabase?=null
fun getInstance(context: Context): TheDatabase {
if (instance==null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java, DATABASE_NAME)
.createFromAsset(ASSET_NAME,pdc)
.allowMainThreadQueries() /* For convenience of the demo */
.addCallback(cb)
.addMigrations(mig1to2)
.build()
}
return instance as TheDatabase
}
private val mig1to2 = object: Migration(1,2) {
override fun migrate(database: SupportSQLiteDatabase) {
Log.d("MIG1-2", "Migration is running")
val cv = ContentValues()
cv.put("word1", "NEWWORD W1=W")
cv.put("word2", "NEWWORD W2=W")
database.insert("word", OnConflictStrategy.IGNORE, cv)
cv.clear()
cv.put("word1", "NEWWORD W1=X")
cv.put("word2", "NEWWORD W2=X")
database.insert("word", OnConflictStrategy.IGNORE, cv)
cv.clear()
cv.put("word1", "NEWWORD W1=Y")
cv.put("word2", "NEWWORD W2=Y")
database.insert("word", OnConflictStrategy.IGNORE, cv)
cv.clear()
cv.put("word1", "NEWWORD W1=Z")
cv.put("word2", "NEWWORD W2=Z")
database.insert("word", OnConflictStrategy.IGNORE, cv)
}
}
val cb = object: RoomDatabase.Callback() {
val TAG = "DBCALLBACK"
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
Log.d(TAG,"onCreate called")
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
Log.d(TAG,"onOpen called")
}
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
super.onDestructiveMigration(db)
Log.d(TAG,"onDestructiveMigration called")
}
}
val pdc = object: PrepackagedDatabaseCallback(){
val TAG = "PPDOPEN"
override fun onOpenPrepackagedDatabase(db: SupportSQLiteDatabase) {
super.onOpenPrepackagedDatabase(db)
Log.d(TAG,"Prepackaged Database has been copied and opened")
}
}
}
}
note that the database version is 1
the callbacks are included to show when and what is called.
the file the_database.db is in the assets folder and has 3 rows as per:-
The activity code used is:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: TheDAOs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getTheDAOs()
for(w in dao.getAllWords()) {
Log.d("DBINFO","Word ID is ${w.id} WORD1 is ${w.word1} WORD2 is ${w.word2}")
}
}
}
i.e. It simply accesses the database, extracts all the rows and writes the data to the log.
When run as a new install at database version 1 then the output to the log is:-
2023-01-02 15:26:47.055 D/PPDOPEN: Prepackaged Database has been copied and opened
2023-01-02 15:26:47.119 D/DBCALLBACK: onOpen called
2023-01-02 15:26:47.124 D/DBINFO: Word ID is 1 WORD1 is ORIGINALWORD1=W1_A WORD2 is ORIGINALWORD2=W2_A
2023-01-02 15:26:47.124 D/DBINFO: Word ID is 2 WORD1 is ORIGINALWORD1=W1_B WORD2 is ORIGINALWORD2=W2_B
2023-01-02 15:26:47.124 D/DBINFO: Word ID is 3 WORD1 is ORIGINALWORD1=W1_C WORD2 is ORIGINALWORD2=W2_C
If run again, still at version 1 then the output is:-
2023-01-02 15:28:27.976 D/DBCALLBACK: onOpen called
2023-01-02 15:28:27.981 D/DBINFO: Word ID is 1 WORD1 is ORIGINALWORD1=W1_A WORD2 is ORIGINALWORD2=W2_A
2023-01-02 15:28:27.981 D/DBINFO: Word ID is 2 WORD1 is ORIGINALWORD1=W1_B WORD2 is ORIGINALWORD2=W2_B
2023-01-02 15:28:27.981 D/DBINFO: Word ID is 3 WORD1 is ORIGINALWORD1=W1_C WORD2 is ORIGINALWORD2=W2_C
i.e. the copy of the prepackaged database wasn't invoked as the database existed and the output is otherwise the same.
If the database version is changed to 2 (no schema changes) and the App is rerun then the output is:-
2023-01-02 15:31:32.464 D/MIG1-2: Migration is running
2023-01-02 15:31:32.529 D/DBCALLBACK: onOpen called
2023-01-02 15:31:32.536 D/DBINFO: Word ID is 1 WORD1 is ORIGINALWORD1=W1_A WORD2 is ORIGINALWORD2=W2_A
2023-01-02 15:31:32.536 D/DBINFO: Word ID is 2 WORD1 is ORIGINALWORD1=W1_B WORD2 is ORIGINALWORD2=W2_B
2023-01-02 15:31:32.536 D/DBINFO: Word ID is 3 WORD1 is ORIGINALWORD1=W1_C WORD2 is ORIGINALWORD2=W2_C
2023-01-02 15:31:32.536 D/DBINFO: Word ID is 4 WORD1 is NEWWORD W1=W WORD2 is NEWWORD W2=W
2023-01-02 15:31:32.536 D/DBINFO: Word ID is 5 WORD1 is NEWWORD W1=X WORD2 is NEWWORD W2=X
2023-01-02 15:31:32.536 D/DBINFO: Word ID is 6 WORD1 is NEWWORD W1=Y WORD2 is NEWWORD W2=Y
2023-01-02 15:31:32.536 D/DBINFO: Word ID is 7 WORD1 is NEWWORD W1=Z WORD2 is NEWWORD W2=Z
i.e. the Migration was invoked and the new data introduced as per the code in the Migration.
If the App is rerun (still at version 2) then :-
2023-01-02 15:34:21.336 D/DBCALLBACK: onOpen called
2023-01-02 15:34:21.342 D/DBINFO: Word ID is 1 WORD1 is ORIGINALWORD1=W1_A WORD2 is ORIGINALWORD2=W2_A
2023-01-02 15:34:21.342 D/DBINFO: Word ID is 2 WORD1 is ORIGINALWORD1=W1_B WORD2 is ORIGINALWORD2=W2_B
2023-01-02 15:34:21.342 D/DBINFO: Word ID is 3 WORD1 is ORIGINALWORD1=W1_C WORD2 is ORIGINALWORD2=W2_C
2023-01-02 15:34:21.342 D/DBINFO: Word ID is 4 WORD1 is NEWWORD W1=W WORD2 is NEWWORD W2=W
2023-01-02 15:34:21.342 D/DBINFO: Word ID is 5 WORD1 is NEWWORD W1=X WORD2 is NEWWORD W2=X
2023-01-02 15:34:21.342 D/DBINFO: Word ID is 6 WORD1 is NEWWORD W1=Y WORD2 is NEWWORD W2=Y
2023-01-02 15:34:21.342 D/DBINFO: Word ID is 7 WORD1 is NEWWORD W1=Z WORD2 is NEWWORD W2=Z
i.e. the Migration isn't invoked and all the data remains.
If the App is uninstalled and then installed/run (as per a new install) then:-
2023-01-02 15:37:25.096 D/PPDOPEN: Prepackaged Database has been copied and opened
2023-01-02 15:37:25.113 D/MIG1-2: Migration is running
2023-01-02 15:37:25.169 D/DBCALLBACK: onOpen called
2023-01-02 15:37:25.175 D/DBINFO: Word ID is 1 WORD1 is ORIGINALWORD1=W1_A WORD2 is ORIGINALWORD2=W2_A
2023-01-02 15:37:25.175 D/DBINFO: Word ID is 2 WORD1 is ORIGINALWORD1=W1_B WORD2 is ORIGINALWORD2=W2_B
2023-01-02 15:37:25.175 D/DBINFO: Word ID is 3 WORD1 is ORIGINALWORD1=W1_C WORD2 is ORIGINALWORD2=W2_C
2023-01-02 15:37:25.175 D/DBINFO: Word ID is 4 WORD1 is NEWWORD W1=W WORD2 is NEWWORD W2=W
2023-01-02 15:37:25.175 D/DBINFO: Word ID is 5 WORD1 is NEWWORD W1=X WORD2 is NEWWORD W2=X
2023-01-02 15:37:25.175 D/DBINFO: Word ID is 6 WORD1 is NEWWORD W1=Y WORD2 is NEWWORD W2=Y
2023-01-02 15:37:25.176 D/DBINFO: Word ID is 7 WORD1 is NEWWORD W1=Z WORD2 is NEWWORD W2=Z
i.e. the prepackaged database has been copied and the migration was invoked thus introducing the new data (which is NOT in the prepackaged database).
Other ways
Another way could be to utilise the version number in the prepackaged database. This would involve accessing the prepackaged via the asset manager extracting the version number (4 bytes at offset 60) from the header (first 100 bytes of the file), comparing it with the version number in the actual database if the former is higher then new data exists. So both databases could be opened the rows copied.
Room uses the version number (user_version), so another take could be to instead use the application id (4 bytes offset 68).
Both of these would require setting the values in the pre-packaged database and updating them along with a new distribution/APK.
Another option could be to a have a core database/file accessible via the internet with a method of detecting a change to the data.
Other ways could be to introduce detection via extra columns and perhaps even an extra table. However, the less the burden, the likelihood the greater the the complexity of the solution.

kotlin android Realm DB create a foreign key (Linking Objects)

I have 2 tables student and a teacher.
The student table has 3 fields, name, roll_no, subjects.
Roll_no being primary key.
Another table named marks with 4 fields subject ID, subject name, subject score and roll_no
roll_no field will be a foreign key.
How to create a relation like the marks table refers as a foreign key to student table's roll_no.
Short answer: Realm doesn't have foreign keys.
Long answer: Realm wants you to think about your data as objects, instead of tables and links (https://realm.io/docs/kotlin/latest/#relationships).
Assuming roll_no is kind of like a student_id - you could model your data like this:
open class Student(
#PrimaryKey
var id: String = "",
var markedSubjects: RealmList<MarkedSubject> = RealmList()
): RealmObject()
open class MarkedSubject(
var subject: Subject? = null,
var mark: Int? = null
): RealmObject()

Sqlite SUM Entity value depending on LEFT JOIN value

I'm using Room on Android to query a POJO.
My POJO (Transaction) contains a value and an account_id. It further contains an account (instance of Account), which is fetched by a #Relation.
The transaction class:
#Parcelize
class Transaction(
#Embedded val entity: TransactionEntity,
#Relation(entity = Account::class, parentColumn = Database.Transactions.COL_ACCOUNT_ID, entityColumn = Database.Accounts.COL_ID) val account: Account? = null
) : Model
The accounts properties:
data class Account(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = Database.Accounts.COL_ID) val id: Long = 0,
#ColumnInfo(name = Database.Accounts.COL_NAME) val name: String = "",
#ColumnInfo(name = Database.Accounts.COL_BALANCE) val balance: Double = 0.0
)
The accounts.balance is not a value in the database, but the sum of all transactions.value belonging to that account.
So I need to sum every transaction.value where transaction.account_id = account.id.
It should work similar to this way, but I'm not getting it right:
#androidx.room.Transaction
#Query(
"""
SELECT transactions.* FROM transactions
LEFT OUTER JOIN (SELECT transactions.value, SUM(transactions.value) AS balance FROM accounts) ON transactions.account_id = accounts.id
GROUP BY transactions.id
"""
)
fun getTrs(): List<Transaction>
try this:
#androidx.room.Transaction
#Query(
"""
SELECT transactions.* FROM transactions
LEFT OUTER JOIN (SELECT transactions.value, SUM(transactions.value) AS balance FROM accounts GROUP BY transactions.id) ON transactions.account_id = accounts.id
"""
)
fun getTrs(): List<Transaction>
You have list all necessary columns of your table transactions in GROUP BY clause.
SELECT transactions.* SUM(transactions.value) AS balance
FROM transactions
LEFT JOIN accounts ON transactions.account_id = accounts.id
GROUP BY ...transactions.id, transactions.account_id.... and all necessary columns of transactions table...
I hope I helped you!
You could use subquery:
SELECT t.*,(SELECT SUM(transactions.value) FROM accounts a ON t.account_id = a.id) AS balance
FROM transactions t
Make it clean:
Create a balance view first
CREATE VIEW v_balance AS
SELECT account_id, SUM(value) as account_balance
FROM transactions
GROUP BY account_id
Then use the view in left join as desired:
SELECT account.*, IFNULL(v_balance.account_balance, 0.0)
FROM account
LEFT JOIN v_balance ON v_balance.account_id = account.id
It's probably meant alike this:
SELECT a.*, SUM(t.value) AS balance
FROM transactions t
LEFT JOIN accounts a
ON a.account_id = t.account_id
GROUP BY t.account_id
I've made it by creating a new POJO wrapping my original account object, but adding a field for the sum which is now queried.
#Query(
"""
SELECT accounts.*,
(SELECT SUM(CASE WHEN transactions.type = ${Transaction.TransactionType.EARNING} THEN transactions.value
ELSE -transactions.value END) FROM transactions WHERE transactions.account_id = accounts.id) AS balance
FROM accounts
LEFT JOIN transactions ON transactions.account_id = accounts.id
GROUP BY accounts.id
"""
)
fun getAccountsWithBalance(): LiveData<List<AccountWithBalance>>

How can delete all records except latest 10 records with in Kotlin with anko?

The Code B define a Log table, I hope to clear all records except latest 10 records.
At present, I list all records order by CreatedDate first, then I do a loop from 11th record to last record, and delete the record using Code A.
Is there a better way to do that in Kotlin with anko ?
Code A
fun deleteDBLogByID(_id:Long)=mDBLogHelper.use{
delete(DBLogTable.TableNAME,"$idName = {$idName} ","$idName" to _id.toString() )
}
Code B
class DBLogHelper(mContext: Context = UIApp.instance) : ManagedSQLiteOpenHelper(
mContext,
DB_NAME,
null,
DB_VERSION) {
companion object {
val DB_NAME = "log.db"
val DB_VERSION = 1
val instance by lazy { DBLogHelper() }
}
override fun onCreate(db: SQLiteDatabase) {
db.createTable( DBLogTable.TableNAME , true,
DBLogTable._ID to INTEGER + PRIMARY_KEY+ AUTOINCREMENT,
DBLogTable.CreatedDate to INTEGER,
DBLogTable.Status to INTEGER +DEFAULT("0"),
DBLogTable.Description to TEXT
)
}
}
Checking the source code of same at below location
https://github.com/Kotlin/anko/blob/e388295c70963d97d26820d4ecdf48ead8dba05e/anko/library/static/sqlite/src/Database.kt#L73
The function definition also takes a whereClause
fun SQLiteDatabase.delete(tableName: String, whereClause: String = "", vararg args: Pair<String, Any>): Int {
return delete(tableName, applyArguments(whereClause, *args), null)
}
Which you can also see in below SO thread
How to delete rows in SQLite with multiple by where args using Anko?
Now combining above and below SO thread
Delete all but top n from database table in SQL
WHERE id NOT IN (SELECT id FROM table ORDER BY id LIMIT n);
You could do something like below
delete(TABLE_NAME, whereClause = "WHERE _ID NOT IN (SELECT _ID FROM {TABLE_NAME} ORDER BY CreatedDate Desc LIMIT {TOP})",
"TOP" to 10,
"TABLE_NAME" to TABLE_NAME)
Above may need small fine tuning if it doesn't work but the approach should work. I don't have Kotlin setup to test and confirm the same. But you can provide feedback if you face an issue
If you're talking about how to do it with the list so it would be more concise, than you can try:
list.filterIndexed({ index, _ -> index > 10 }).forEach { delete(it) }

Categories

Resources