How can I reset a autoincrement sequence number in sqlite - android

How to update table sqlite_sequence in Ormlite ? I just need update seq. How can I get that table via ORMLite ?
EDIT
I can't find ORLite tool to do this, so instead I use simple sqlite query. In my class extends OrmLiteSqliteOpenHelper I use SQLiteDatabase to make that update.
EDIT2 ;)
In my project I persist class Lesson and class WeekDefinition.
class Lesson{
#DatabaseField(generatedId=true)
private int id;
...
}
class WeekDefinitions{
#DatabaseField(generatedId=true)
private int id;
#DatabaseField(foreign=true, columnName="lesson_id")
private Lesson lesson;
...
}
Now , when I add new lessons, id is increment. For example
id = 1 Math
id = 2 English
id = 3 Medicine
and in weekDefinition :
id = 1 lesson_id = 1 nr = 20
id = 2 lesson_id = 1 nr = 22
id = 3 lesson_id = 2 nr = 32
...
id = 12 lesson_id = 3 nr = 3
SQLite add this row into sqlite_sequence ( when use autoincrement )
rowId = 1 name = lesson seq = 3
rowId = 2 name = weekDefinition seq = 12
Now, I delete all rows from tables Lesson and WeekDefinition. Lesson and WeekDef are empty after that, but sqlite_sequence is still the same. And this is problem because id in table lesson start from value 4 ( seq from sqlite_sequence for lesson and add 1 ) :
id = 4 Math
id = 5 English
id = 6 Medicine
and weekDefinition
id = 13 lesson_id = 1 nr = 20
id = 14 lesson_id = 1 nr = 22
id = 15 lesson_id = 2 nr = 32
and for lesson id = 4 , Math i should get weekDefinitios, but in weekDefinitions lessons_id has value only from 1 to 3
And this is my problem. I need "reset" sqlite_sequence table ( or there is better solution ?)

Building on Marcos Vasconcelos' answer:
UPDATE sqlite_sequence SET seq = (SELECT MAX(col) FROM Tbl) WHERE name="Tbl"
This query will set seq to the largest value in the col identity column in the Tbl table, so there is no risk of violating constraints.

Inside your .db file there's an table called sqlite_sequence
Each row has two columns
name which is the name of the table
seq a integer indicating the current last value at this table
You can update it to 0
But beware if your table use this id as the unique identifier.

UPDATE SQLITE_SEQUENCE SET SEQ= 'value' WHERE NAME='table_name';

If you want to issue general database commands in ORMLite, you can use the updateRaw method. See the javadocs. There is also executeRaw for other commands.
lessonDao.updateRaw("delete from 'lesson';");
lessonDao.updateRaw("delete from sqlite_sequence where name='lesson';");
weekDefinitionDao.updateRaw("delete from 'weekdefinition';");
weekDefinitionDao.updateRaw(
"delete from sqlite_sequence where name='weekdefinition';");
You could also drop and recreate the table as well:
TableUtils.dropTable(WeekDefinition.class);
TableUtils.dropTable(Lesson.class);
TableUtils.createTable(Lesson.class);
TableUtils.createTable(WeekDefinition.class);
I think the real question is why is your application depending on this database internal number? It really shouldn't care.
How about not displaying the number at all so it can be 1 or 1001 and your application won't matter?
You could also never remove the lessons at all but maybe add a hidden boolean field. So if they get re-added, the hidden field could be set to false and Math would still be at id #1.

This worked for me in my database : (I Set the id before one, then after deleting one row, when I add new data row again, the auto increment serial remain ok ) :
public void updateSerialNumber ( long memberId){
String query = "UPDATE SQLITE_SEQUENCE SET SEQ= '"+(memberId-1)+"' WHERE NAME='"+ReportBigHelper.TABLE_MEMBER+"'";
database.execSQL(query);
}

Related

Failed rename column while join query

I have a query:
#Query("SELECT entries.*, myProfile.myProfileId as my_profile_id FROM Entry as entries LEFT JOIN MyProfile ON entries.user_public_profile_userId = myProfile.myProfileId")
I just need to get value from second(right) table and put it into entitys' field - "my_profile_id" from first table. But nothing works.
this is how field from first table(left) entity look like -
#ColumnInfo(name = "my_profile_id")
val myUserProfileId: Int? = null,
and this is how it look like this field from second table (right)
#PrimaryKey
val myProfileId: Int,
Result is always null, but condition after ON is working because i tried to change from LEFT JOIN to INNER JOIN ad got results, so the only problem here is to map correctly 'myProfileId' into 'my_profile_id'
What am i doing wrong?
Irrespective of the JOIN type using :-
SELECT entries.*, myProfile.myProfileId as my_profile_id FROM Entry as entries LEFT JOIN MyProfile ON entries.user_public_profile_userId = myProfile.myProfileId
Will result in my_profile_id being the same value that is stored in the user_public_profile_userId column. In that sense the JOIN is a waste of time.
I suspect that you may want to get another useful value from the MyProfile table.
Assuming that you have an Entry entity that is along the lines of :-
#Entity
data class Entry(
#PrimaryKey
val id: Long? = 0,
val name: String,
#ColumnInfo(name = "my_profile_id")
val myUserProfileId: Long? = null,
val user_public_profile_userId: Long
)
And an MyProfile entity that is along the lines of :-
#Entity
data class MyProfile(
#PrimaryKey
val myProfileId: Long? = null,
val otherdata: String
)
and that you want to get the value from the otherdata column then you need an additional POJO to combine the data.
As such consider such a POJO EntryPlus :-
data class EntryPlus(
#Embedded
val entry: Entry,
val profileIdFromMyProfile: Long,
val otherdataFromMyProfile: String
)
#Embedded and the following line is saying that you want all the columns/fields from the Entry table
The other two columns will be from elsewhere (satisfied by the query)
So you could have a Query such as :-
#Query("SELECT entries.*, myProfile.myProfileId AS profileIdFromMyProfile, myProfile.otherdata AS otherdataFromMyProfile FROM Entry as entries LEFT JOIN MyProfile ON entries.user_public_profile_userId = myProfile.myProfileId")
fun getMyOtherData(): List<EntryPlus>
i.e. the query is little different BUT importantly uses AS to name the output columns accordingly to suit the names of the fields in the EntryPlus POJO.
also importantly the result is a list of the POJO (EntryPlus).
Example
Consider the following code that:
inserts some data (3 MyProfile rows and 2 Entry rows) and then
extracts All the Entry rows with no JOIN using SELECT * FROM entry and then
extracts using your original query and then
extracts via the POJO
The code is :-
db = TheDatabase.getInstance(this) // Get database instance
dao = db.getAllDao() // get the Dao
dao.deleteAllMyProfile() // Clear the MyProfile table
dao.deleteAllEntry() // Clear the Entry table
// Add some profile rows
dao.insert(MyProfile(1,"myprofile1"))
dao.insert(MyProfile(2,"myprofile2"))
dao.insert(MyProfile(3,"myprofile3"))
// Add some Entry rows (both link to profile1 in this case)
dao.insert(Entry(100,"Entry1",0,1))
dao.insert(Entry(200,"Entry2",0,1))
// Extract 1 All as Entry List (no join)
for(e: Entry in dao.getAll()) {
Log.d("ENTRYINFO(1)","Entry Name is ${e.name} EntryID is ${e.id} MapToMyProfile is ${e.user_public_profile_userId} Value is ${e.myUserProfileId}" )
}
// Extract 2 All from original query
for(e: Entry in dao.getMyData()) {
Log.d("ENTRYINFO(2)","Entry Name is ${e.name} EntryID is ${e.id} MapToMyProfile is ${e.user_public_profile_userId} Value is ${e.myUserProfileId}" )
}
// Extract 3 getting useful data from the 2nd (JOINED) table
for(ep: EntryPlus in dao.getMyOtherData()) {
Log.d("ENTRYINFO(3)",
"Entry Name is ${ep.entry.name} EntryID is ${ep.entry.id} MapToMyProfile is ${ep.entry.user_public_profile_userId} Myuserprofile(From Entry) ${ep.entry.myUserProfileId}" +
" MyProfileId (From MyProfile) is ${ep.profileIdFromMyProfile} OtherData (From MyProfile) is ${ep.otherdataFromMyProfile}" )
}
The output to the Log is :-
2021-07-07 09:44:12.665 D/ENTRYINFO(1): Entry Name is Entry1 EntryID is 100 MapToMyProfile is 1 Value is 0
2021-07-07 09:44:12.665 D/ENTRYINFO(1): Entry Name is Entry2 EntryID is 200 MapToMyProfile is 1 Value is 0
2021-07-07 09:44:12.666 D/ENTRYINFO(2): Entry Name is Entry1 EntryID is 100 MapToMyProfile is 1 Value is 1
2021-07-07 09:44:12.666 D/ENTRYINFO(2): Entry Name is Entry2 EntryID is 200 MapToMyProfile is 1 Value is 1
2021-07-07 09:44:12.667 D/ENTRYINFO(3): Entry Name is Entry1 EntryID is 100 MapToMyProfile is 1 Myuserprofile(From Entry) 0 MyProfileId (From MyProfile) is 1 OtherData (From MyProfile) is myprofile1
2021-07-07 09:44:12.668 D/ENTRYINFO(3): Entry Name is Entry2 EntryID is 200 MapToMyProfile is 1 Myuserprofile(From Entry) 0 MyProfileId (From MyProfile) is 1 OtherData (From MyProfile) is myprofile1
Notes on the Output
The first two lines note that the MyProfileId value (i.e. Value =) is 0 as was inserted.
The Second two lines, using your original query shows that MyProfileId (Value =) is now the same value as the link/reference/association/relationship (i.e. the user_public_profile_userId column) to the MyProfile row.
The Third shows that the values from the MyProfile table (the otherdata column) have been extracted.
However, you are also appearing to describe the nature of LEFT JOIN in comparison to just JOIN.
If the following line (another Entry row but referencing a non-existent MyProfile row) were added before the extract:-
dao.insert(Entry(300,"Entry3",999,10 /* 10 = No such myprofile row */))
then the changes in the result will be significant in that the 2nd extract retrieves null for the my_profile_id as per :-
D/ENTRYINFO(2): Entry Name is Entry3 EntryID is 300 MapToMyProfile is 10 Value is null
changing to just JOIN (not LEFT JOIN) and the row which references the non-existent MyProfile is omitted. This is the documented impact as per :-
If the join-operator is a "LEFT JOIN" or "LEFT OUTER JOIN", then after the ON or USING filtering clauses have been applied, an extra row is added to the output for each row in the original left-hand input dataset that corresponds to no rows at all in the composite dataset (if any). The added rows contain NULL values in the columns that would normally contain values copied from the right-hand input dataset.
SQLite SELECT
In the case of null values, if this is your issue, then you need to decide what to do.
You could for example use the SQLite COALESCE function to change the null. e.g. SELECT entries.*, coalesce(myProfile.myProfileId,9999) as my_profile_id FROM Entry ....
Perhaps you need to ensure that the referential integrity is maintained, in which case you can utilise FOREIGN KEYS to enforce referential integrity. Perhaps refer to Foreign Key

Android exception in sql query that runs fines in DB Browser

When running the following query on Android
select *
from EICRChecklistItems
where (Sect, S_Sect, SS_Sect) in (
select Sect, S_Sect, SS_Sect
from EICRCheckList
where Version=2018
order by id
)
order by id
I get the exception:
android.database.sqlite.SQLiteException: near ",": syntax error (code
1)
Columns Sect, S_Sect and SS_Sect are all defined as integer.
But when running on DB Browser against a copy of the same database it executes correctly
Any pointers as to what may be causing this would be greatly appreciated.
private var EICR_CHECK_ITEMS_TABLE = "EICRChecklistItems"
private var EICR_CHECKLIST_TABLE = "EICRCheckList"
val cursor1: Cursor = writableDatabase.rawQuery(
"select * from $EICR_CHECK_ITEMS_TABLE where (Sect, S_Sect, SS_Sect) in (select Sect, S_Sect, SS_Sect from $EICR_CHECKLIST_TABLE where Version='2018' order by id) order by id", null)
Since your SQLite's version is prior to 3.15.0 and you can't use ROW VALUES, you can write your query with EXISTS instead of the operator IN:
SELECT ei.*
FROM EICRChecklistItems ei
WHERE EXISTS (
SELECT 1 FROM EICRCheckList el
WHERE el.Version = 2018
AND el.Sect = ei.Sect AND el.S_Sect = ei.S_Sect AND el.SS_Sect = ei.SS_Sect
)
ORDER BY ei.id
Note that the ORDER BY clause that you had inside the subquery in your code is actually useless.

Select a Random row in realm table

I want to select a random row from the realm table. Something like -
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
Something like this would do, yes?
Random random = new Random();
RealmResults<YourTable> list = realm.where(YourTable.class).findAll();
YourTable yourTable = list.get(random.nextInt(list.size()));
Depends on what you want to do:
Do you want to get a random row from a table?
Or A (random) row from a random table?
I guess you mean the former:
If you have an id in your table, you could just:
SELECT * FROM table b WHERE id = FLOOR(RAND() * (3 - 0 + 1)) + 0
You should place a min and max here like so:
FLOOR(RAND() * (<max> - <min> + 1)) + <min>
(as found here)
SWIFT 5
I do it this way and it works perfect:
let results = realm.objects(MyObject.self) // Get all the objects
let randomIndex = Int.random(in: 0 ..< results.count) // Get a random number within the number of objects returned
let randomObject = results[randomIndex] // Get a random object
Here's how I do it in .NET, takes 7ms for 70K entries.
public IEnumerable<Entry> GetRandomEntries()
{
var randomizer = new Random();
var entries = GetRealmInstance().All<Entry>().AsEnumerable();
// Takes random entries and shuffles them to break the natural order
var randomEntries = entries
.OrderBy(x => randomizer.Next(0, DbQueryRandomEntriesLimit))
.Take(DbQueryRandomEntriesLimit)
.OrderBy(entry => entry.GetHashCode());
return randomEntries;
}
I use AsEnumerable() to allow using .Select in LINQ
String query =String.format("SELECT * FROM realm ORDER BY %d LIMIT 1", random());
databaseHelper = new DatabaseHelper(this);
database = databaseHelper.getWritableDatabase();
Cursor cursor = database.execSQL(query);
It works assuming that you have a class DatabaseHelper which extends SQLiteOpenHelper

SQL MAX-MIN in ORMLITE - ANDROID

I want to know how can i use MAX MIN command with ORMLITE.
For example lets say we have this table
Table Name = Example
Column 1 = id
Column 2 = name
In ORMLITE how can i get max id ? I looked here but i didnt't understand exactly..
Can someone show me example about Max min in ORMLITE ?
QueryBuilder<Account, Integer> qb = accountDao.queryBuilder();
qb.selectRaw("MIN(orderCount)", "MAX(orderCount)");
// the results will contain 2 string values for the min and max
results = accountDao.queryRaw(qb.prepareStatementString());
String[] values = results.getFirstResult();
I found this from documentation
This is how I query for max ID in my code:
QueryBuilder<Example, String> builder = dao.queryBuilder();
builder.orderBy("id", false); // true or false for ascending so change to true to get min id
Example example = dao.queryForFirst(builder.prepare());
String id = null;
if (example == null)
id = "-1";
else
id = example.getId();
A couple of alternative answers can also be found here:
ORMLite - return item w/ maximum ID (or value)
You can use:
dao.queryRawValue("select MAX(columnName) from tableName")
It will directly return long value.
refer: http://ormlite.com/javadoc/ormlite-core/doc-files/ormlite_5.html#DAO-Methods
queryRawValue(String query, String... arguments)
Perform a raw query that returns a single value (usually an aggregate function like MAX or COUNT). If the query does not return a single long value then it will throw a SQLException.

How do I create a group with a source_id reference?

Update
I finally figured this one out:
The key is to create the group with an account name and account type, something that is not really apparent looking at the documentation (there are no fields with those name in ContactsContract.Groups they are in the SyncColumns). When you create the group with those two values, the sync process will generate the source_id for you, at which point you can add member using either the group row id or the source_id.
Here is some sample code if anyone needs it.
ContentValues values = new ContentValues();
values.put(ContactsContract.Groups.TITLE,"yourGroupName");
values.put(ContactsContract.Groups.ACCOUNT_TYPE,"com.google");
values.put(ContactsContract.Groups.ACCOUNT_NAME,"someuser#gmail.com");
values.put(ContactsContract.Groups.GROUP_VISIBLE,1);
context.getContentResolver().insert(ContactsContract.Groups.CONTENT_URI, values);
then to add members:
values = new ContentValues();
values.put(ContactsContract.CommonDataKinds.GroupMembership.MIMETYPE,ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.GroupMembership.RAW_CONTACT_ID, 22);
//values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID, 56);
// the above or
values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_SOURCE_ID,"sourceIdthatIsFilledInAfterSync");
//context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI,values);
Original question:
I have been struggling with an issue related to syncing group memberships and think I figured out the cause, but I don't know how to fix it.
After creating 2 groups , one via code the the other manually and then query the groups using these columns.
private final static String[] GROUP_COLUMNS = {
ContactsContract.Groups._ID,
ContactsContract.Groups.DATA_SET,
ContactsContract.Groups.NOTES,
ContactsContract.Groups.SYSTEM_ID,
ContactsContract.Groups.GROUP_VISIBLE,
ContactsContract.Groups.DELETED,
ContactsContract.Groups.SHOULD_SYNC,
ContactsContract.Groups.SOURCE_ID,
ContactsContract.Groups.TITLE
};
I can dump out the results as this.
: --- begin ---
: key = title , value = myNewTestGroup
: key = data_set , value = null
: key = _id , value = 45
: key = sourceid , value = null
: key = group_visible , value = 1
: key = system_id , value = null
: key = should_sync , value = 1
: key = notes , value = myNewTestGroup
: key = deleted , value = 0
: --- end ---
: --- begin ---
: key = title , value = Mytest2
: key = data_set , value = null
: key = _id , value = 46
: key = sourceid , value = 144c8b8d0cca8a52
: key = group_visible , value = 1
: key = system_id , value = null
: key = should_sync , value = 1
: key = notes , value = Mytest2
: key = deleted , value = 0
: --- end ---
The manually create group (Mytest2) has a souceid which is listed as a column in ContactsContract.SyncColumns, while the code generated group has null.
I see references to source_id in may places in the android docs but I can't see how to obtain one.
I think somehow i would get this if i associate the group with an account.
Does anyone know how to associate at group with an account, or otherwise get this source id field set?
I finally figured this one out: The key is to create the group with an account name and account type, something that is not really apparent looking at the documentation (there are no fields with those name in ContactsContract.Groups they are in the SyncColumns). When you create the group with those two values, the sync process will generate the source_id for you, at which point you can add member using either the group row id or the source_id.
Here is some sample code if anyone needs it.
ContentValues values = new ContentValues();
values.put(ContactsContract.Groups.TITLE,"yourGroupName");
values.put(ContactsContract.Groups.ACCOUNT_TYPE,"com.google");
values.put(ContactsContract.Groups.ACCOUNT_NAME,"someuser#gmail.com");
values.put(ContactsContract.Groups.GROUP_VISIBLE,1);
context.getContentResolver().insert(ContactsContract.Groups.CONTENT_URI, values);
then to add members:
values = new ContentValues();
values.put(ContactsContract.CommonDataKinds.GroupMembership.MIMETYPE,ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.GroupMembership.RAW_CONTACT_ID, 22);
//values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID, 56);
// the above or
values.put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_SOURCE_ID,"sourceIdthatIsFilledInAfterSync");
context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI,values);
The data in sourceid still needs to be set in the second example before you are able to retrieve it.
here is what the docs have to say about sourceid:
String SOURCE_ID read/write:
String that uniquely identifies this row to its source account.
Typically it is set at the time the raw contact is inserted and never
changed afterwards. The one notable exception is a new raw contact: it
will have an account name and type (and possibly a data set), but no
source id. This indicates to the sync adapter that a new contact needs
to be created server-side and its ID stored in the corresponding
SOURCE_ID field on the phone.

Categories

Resources