I have table with duplicate id record.I written a query to get all record with same id.Now i want to update record id such as follows
CREATE TABLE Student1
(`id` int,`status` int,`amount` int , `Name` varchar(10), `date` varchar(55))
;
INSERT INTO Student1
(`id`,`status`,`amount`, `Name`, `date`)
VALUES
(1,0,4500, 'ram', '04/02/2012'),
(2,0,2000, 'shyam', '05/09/2013'),
(2,0,6500, 'radhe', '11/11/2014'),
(2,0,8000, 'radheshyam', '15/11/2013'),
(4,0,1500, 'ghanshyam', '08/11/2014')
;
id status amount Name date
1 0 4500 ram 04/02/2012
2 0 2000 shyam 05/09/2013
2 0 6500 radhe 11/11/2014
2 0 8000 radheshyam 15/11/2013
4 0 1500 ghanshyam 08/11/2014
SqlQuery:
SELECT * FROM Student1
where id in (SELECT id FROM Student1 GROUP BY id HAVING count(*)>1)
Expected Result :
id status amount Name date
2 0 2000 shyam 05/09/2013
2 0 6500 radhe 11/11/2014
2 0 8000 radheshyam 15/11/2013
Now i want to update any two records id's to 21,211.So i am trying to get the cursor count which will return 3.After getting count i m moving to position 2 to change its id but how to write query to update current cursor position record.
I din't test it, but this will get you going.
cursor.moveToFirst();
while ( !cursor.isAfterLast()) {
// update whatever you want here
db.getWritableDatabase().update(
//TABLE_NAME,
//values,
//whereclause,
//args
);
cursor.moveToNext()
}
Get the values from the cursor on each position and make the updates. This operation should be done outside the main thread. You could use a asynctask.
But you shouldn't create DB records with ID duplication. Add a AUTOINCREMENT statement at you table creation.
No way to update the records in this way as there is no primary key field to use the where clause on. Always mark the id field as primary key to avoid such situations. The best you can do is find these entries, store the Date or name, which ever is unique, and then use that in the where clause to change the id.
do(cursor.moveToFirst()) {
//store the name or Date and call a method on the database
// to update the entry to set new id where name equals to this name.
} while(cursor.moveToNext());
Avoid such situations by marking the id field as primary key. SQLite auto-increments the primary key field even if it is not explicitly mentioned.
Related
I have a table called "users".
id name
1 jack
2 lisa
I want to add a new column and set the default value of it to the value of the "id" column.
ALTER TABLE users ADD COLUMN user_index INTEGER NOT NULL DEFAULT id;
since, the DEFAULT keyword, only accepts constant values the above code doesn't work.so, how can I set the default value of the new column to the value of "id" column?
This can't be done with just the ALTER statement. I would recommend performing the alteration and a copy in a transaction, where the copy would look something like:
UPDATE tableName SET user_index = id;
I believe that the following demo SQL will do what you want.
/*
Create and populate inital table
*/
DROP TRIGGER IF EXISTS set_user_index_as_id;
DROP TABLE IF EXISTS users; -- Note will drop the trigger
CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, `name` TEXT);
INSERT INTO users (`name`) VALUES('jack'),('lisa');
-- Show all columns of all rows of the initial table
SELECT * FROM users;
-- Add the new column with default value of -1 (should not interfere with id assuming it is never -1)
-- ONE-OFF SCHEMA CHANGE
-- Note sets default for rows previously added to default
ALTER TABLE users ADD COLUMN user_index INTEGER NOT NULL DEFAULT -1;
-- Show all columns of all rows after the alter
SELECT * FROM users;
-- Add the trigger to adjust the user_index value whenever a row is inserted
CREATE TRIGGER IF NOT EXISTS set_user_index_as_id AFTER INSERT ON users
BEGIN
-- update rows that have not been updated i.e. user_index is -1
-- (so will adjust the original rows)
UPDATE users SET user_index = id WHERE user_index = -1;
END
;
-- Add some more rows (i.e.test the above)
INSERT INTO users ('name') VALUES('fred'),('mary');
-- Show Show all columns of all rows after the inserts
SELECT * FROM users;
This sets the default value to -1 (assuming that id will never be -1, it could be another value that id would never be that value) when ALTERing the table to add the column. A TRIGGER is then added that will update any rows that have -1 in the user_index column to be the same value as the id column whenever a new row is inserted.
as all rows with -1 are updated the first insert after the trigger has been added will update any previously inserted rows (the default value is retrospectively applied to old rows)
Note The above is a demo and has been designed to rerun, hence the DROP statements.
When run the queries, that show the progress, are :-
After initial inserts
After the Alter
After the new inserts
The above assumes that id is an alias of the rowid (what some term as AUTOINCREMENT) and hence the rows are in monotonically increasing order (increase from 1 to 2 to 3 ....).
However, say the first two rows had id values of 10 and 50 and then the new rows 51 and 52. These out of sequence values would be applied. A test very similar to the above, just changing the first insert to INSERT INTO users VALUES(10,'jack'),(50,'lisa');
has the final result, as expected, of :-
Android SQLite version is 3.19 and doesn't support windows function like over and row_number(). I don't have any auto incremented column. I have created view using data from different table. My view details are below.
1 means in stock
2 means out stock
3 means reset count and start from given quantity.
uniquekey is unique id for each row
I want to get running total as below:
uniquekey date ProductName uniqueKeyProduct InOutType quantity runningTotal
edfrgdctydkkc 2020-06-07 Apple dheykdhr 1 10 10
edfrgdctkduxc 2020-06-08 Orange xdefrttk 1 20 20
fdfrgdctydysc 2020-06-08 Apple dheykdhr 2 5 5
3dfrgrtkvctyf 2020-06-08 Apple dheykdhr 1 2 7
ctgrteerylkdc 2020-06-09 Orange xdefrttk 2 8 12
edffjritydmnc 2020-06-10 Orange xdefrttk 3 5 5
kkdjdjrgdctyk 2020-06-10 Apple dheykdhr 3 2 2
egdhgdctyjjdc 2020-06-11 Orange xdefrttk 1 20 25
edfryrytymnbc 2020-06-15 Apple dheykdhr 1 10 12
fgeegdctydk3c 2020-06-18 Apple dheykdhr 2 2 10
hyidfhhhfd89c 2020-06-20 Orange xdefrttk 2 8 17
I don't have any auto incremented column
Assuming that you haven't defined the table using WITHOUT ROWID then you still have an equivalent column, namely the rowid column.
AUTOINCREMENT is an alias of the rowid column, it is inefficient and rarely required
You may wish to read https://www.sqlite.org/rowidtable.html
I want to get running total as below:
As the date column does not determine the insert order (e.g. 2020-06-08 2 Apple rows were processed) the rowid column has been utilised (not 100% failsafe in this regard).
You may wish to consider making the date a column that holds the date and time of the insert.
One way to accomplish the above would be to utilise a TRIGGER. Triggers are driven by an INSERT UPDATE or DELETE event. So it makes sense to use a trigger whenever a row is inserted to then set the runningtotal.
The following TRIGGER maintains the running total for the data that you have provided.
CREATE TRIGGER IF NOT EXISTS mytable_after_insert
AFTER INSERT ON mytable
BEGIN
UPDATE mytable SET runningtotal =
CASE
/* in stock */
WHEN new.inouttype = 1 THEN
COALESCE(
(
SELECT mt1.runningtotal
FROM mytable AS mt1
WHERE mt1.rowid < new.rowid AND mt1.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
+ new.quantity
/* out stock */
WHEN new.inouttype = 2 THEN
COALESCE(
(
SELECT mt2.runningtotal
FROM mytable AS mt2
WHERE mt2.rowid < new.rowid AND mt2.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
- new.quantity
/* reset */
WHEN new.inouttype = 3 THEN new.quantity
END
WHERE rowid = new.rowid
;
END
;
note that the table name mytable will likely have to be changed. Perhaps the trigger's name should be changed as well.
The following is the SQL used to test the above. It
Drops the Trigger
Drops the Table
Creates the table (this may need to be amended appropriately)
Creates the Trigger
Inserts all the rows (note that the runningtotal column defaults to 0)
Queries the resultant table showing the runningtotal and also the rowid
:-
DROP TRIGGER IF EXISTS mytable_after_insert;
DROP TABLE IF EXISTS mytable;
CREATE TABLE IF NOT EXISTS mytable
(
uniquekey TEXT PRIMARY KEY,
date TEXT,
productname TEXT,
uniquekeyproduct TEXT,
inouttype INTEGER,
quantity INTEGER,
runningtotal INTEGER DEFAULT 0
)
;
CREATE TRIGGER IF NOT EXISTS mytable_after_insert
AFTER INSERT ON mytable
BEGIN
UPDATE mytable SET runningtotal =
CASE
/* in stock */
WHEN new.inouttype = 1 THEN
COALESCE(
(
SELECT mt1.runningtotal
FROM mytable AS mt1
WHERE mt1.rowid < new.rowid AND mt1.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
+ new.quantity
/* out stock */
WHEN new.inouttype = 2 THEN
COALESCE(
(
SELECT mt2.runningtotal
FROM mytable AS mt2
WHERE mt2.rowid < new.rowid AND mt2.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
- new.quantity
/* reset */
WHEN new.inouttype = 3 THEN new.quantity
END
WHERE rowid = new.rowid
;
END
;
INSERT INTO mytable (
uniquekey,date,productname,uniquekeyproduct,inouttype,quantity /* running total not supplied so defaults to 0 */
)
VALUES
('edfrgdctydkkc','2020-06-07','Apple','dheykdhr',1,10),
('edfrgdctkduxc','2020-06-08','Orange','xdefrttk',1,20),
('fdfrgdctydysc','2020-06-08','Apple','dheykdhr',2,5),
('3dfrgrtkvctyf','2020-06-08','Apple','dheykdhr',1,2),
('ctgrteerylkdc','2020-06-09','Orange','xdefrttk',2,8),
('edffjritydmnc','2020-06-10','Orange','xdefrttk',3,5),
('kkdjdjrgdctyk','2020-06-10','Apple','dheykdhr',3,2),
('egdhgdctyjjdc','2020-06-11','Orange','xdefrttk',1,20),
('edfryrytymnbc','2020-06-15','Apple','dheykdhr',1,10),
('fgeegdctydk3c','2020-06-18','Apple','dheykdhr',2,2),
('hyidfhhhfd89c','2020-06-20','Orange','xdefrttk',2,8)
;
SELECT rowid,* FROM mytable ORDER BY rowid ASC
The result being :-
Note that if normalisation rules were followed then there should be
another table for the products and thus that the product name would
not be duplicated, all that would be needed was the uniquekeyproduct.
e.g. if a product name were changed then all rows in the mytable table would have to be updated whereas if there were a product table a name changed would be just need the 1 row to be updated and it would be reflected when querying mytable (the ProductName column would no longer be required).
COALESCE is used to convert the null returned when the first row for a product is inserted as there are no rows that match the selection criteria for in stock and out stock actions. see https://www.sqlitetutorial.net/sqlite-functions/sqlite-coalesce/
CASE ..... END is for conditional processing of the inouttype column. It has 3 WHEN clauses one for each of the types (1,2 or 3). see https://www.sqlite.org/lang_expr.html#case
In the subqueries used within the first two WHEN clauses tables are given a temporary name mt1 and mt2 respectively, using AS to ensure that there is no ambiguity as to where the data is to come from.
Note that the as 3.19 is quite old the above was tested on more recent versions so there may be some issues. You may wish to have a look through https://www.sqlite.org/changes.html if you come across issues.
I use this method to delete a row in my sqlite db:
db.execSQL("delete from "+TABLE_NUMS+" where _ID = '" + this.rowID + "'");
and then I update the rest of Ids to make my entries consecutive:
db.execSQL("UPDATE "+TABLE_NUMS+" set _ID = (_ID - 1) WHERE _ID > "+this.rowID);
And it works fine, but when I add new entries to my DB, the ID of the new entries still add as if the deleted entries existed, say I have 10 rows with IDs starting from 1 to 10, and then I delete number 5 and 6, the rows become 1 to 8, but the new entry's ID will be 11. So my IDs sequence would be 1 to 8 and 11. How can I fix this?
SQLite keeps track of the largest ROWID that a table has ever held using the special SQLITE_SEQUENCE table. You cam modify that sequence as:
UPDATE SQLITE_SEQUENCE SET seq = this.ID -1 WHERE name = TABLE_NUMS
The same functionality is asked in this question.
The normal ROWID selection algorithm described above will generate
monotonically increasing unique ROWIDs as long as you never use the
maximum ROWID value and you never delete the entry in the table with
the largest ROWID. If you ever delete rows or if you ever create a row
with the maximum possible ROWID, then ROWIDs from previously deleted
rows might be reused when creating new rows and newly created ROWIDs
might not be in strictly ascending order.
http://www.sqlite.org/autoinc.html
This is how SQLite works.
If you really need to have the Ids consecutive don't use autoincrement.
Insert the ids yourself.
You can select MAX(_ID) first to get the last id (greatest value).
This is because you have autoincrement set on _ID when you created the table. So, every row you add will be given a number automatically unless you explicitly set it. If it is absolutely necessary that you need the _IDs in consecutive order, I recommend that you set it yourself instead of using autoincrement.
Here is how to reset it:
delete from your_table;
delete from sqlite_sequence where name='your_table';
This will delete all your data and reset the sequence.
SQLite keeps the largest ROWID in the special SQLITE_SEQUENCE table. You can update that table as:
db.execSQL("UPDATE SQLITE_SEQUENCE SET seq = 0 WHERE NAME = '"+TABLE_NAME+"'");
OR
delete that table as:
db.delete("SQLITE_SEQUENCE","NAME = ?",new String[]{TABLE_NAME});
I have a table with the following schema:
CREATE TABLE table (
msg_id TEXT,
thread_id TEXT,
.
.
.
date INTEGER
)
I need to retrieve the most recent n msg_id per unique value of thread_id. Is there a way to do it using a single query or will I need to query the database to get the most recent distinct thread_ids, then query the database again PER unique thread_id? I recall reading somewhere that multiple database queries can get expensive.
You could use a correlated subquery. For example, for N = 5 :
select *
from YourTable yt1
where 5 <
(
select count(*)
from YourTable yt2
where yt2.thread_id = yt1.thread_id
and yt2.msg_id < yt1.thread_id
)
This is not too fast, so you might be better of with multiple queries.
I have a database that stores the rank of an Item.
The rank is an absolute value that will be correct if all the items are taken into account.
If I only need a subset say four of this items it will give me something like:
Rank RowId in the whole Table
---------------
4 114
8 71
70 16
83 7
I now need an int specifying the rank only in the subset where the max rank is the number of items in the subset in my example 1,2,3,4.
Is there a way to achieve this in my sqlite query? I only need one of the ranks in my Activity. I thought of ordering the results of the query by rank and then somehow get the position of item I want to rank at that moment. But how would I achieve this with sqlite?
I tried to create a temporary table and insert the subset into it like this:
CREATE TABLE rank(ID);
INSERT INTO position SELECT ID from items WHERE ITEM_ID = 3 ORDER BY POSITION;
SELECT RowID from rank WHERE ID = 9;
DROP TABLE rank;
This is working in SQLite Manager and will return the correct number. But if I do this in Android in fails saying that there is no table rank while compiling query
07-07 13:35:46.150: ERROR/AndroidRuntime(2047): Caused by: android.database.sqlite.SQLiteException: no such table: rank: , while compiling: SELECT RowID from rank WHERE ID = 9
EDIT: have to agree with #Matt the only way I've been able to do this is to use the temp table approach.
For what it's worth here's what it looks like...
create temp table if not exists <temptable>(Id integer primary key, rank);
insert into temptable(rank) select <column> from <table>;
select * from temptable;
EDIT: Actually that returns the ID associated with the row which isn't sequential so you won't always get 1,2,3,4... I'll have to think of something else. Sorry.
Not sure if I've understood your question. You basically want this?
Id Value
---------------
1 4
2 8
3 70
4 83
So you want to add a pseudo-column as the id no matter what your subset contains?
If that's correct then this should do it...
SELECT RowId, <other columns>.... FROM <table> WHERE <where>
Apologies if I've misunderstood.
You could output your query (ordered by rank) into a temporary table with an auto increment ID.
If you need to read only one row from a subquery you can always execute a limit on it, by providing the offset of how many records to be skipped first, and how much to be returned
so if you want to get 25th row you tell to skip 24, and return 1
select * from (SELECT * FROM table order by rank) limit 24,1