I have an Android Sqlite database and I'm trying to select the top 1000 rows by RANK number, because RANK is always changing I sometimes get duplicate rows with the same RANK number, what I would like to do is only keep the duplicate row containing the newest RANK based on its CREATED_DATE, I will visually display this below:
id rank created_date
1 1 1/1/2014
2 2 1/1/2021
3 3 1/1/2021
4 1 1/1/2021
The output I want is:
id rank created_date
2 2 1/1/2021
3 3 1/1/2021
4 1 1/1/2021
My current code gets close but doesn't remove duplicate RANK based on CREATED_DATE instead it removes them based on ID which I don't want and I haven't been able to find a way to do it by CREATED_DATE
Cursor c = theDatabase.query(DATABASE_TABLE, columns, RANK + " BETWEEN 1 AND 1000", null,
RANK, null, ID + " ASC");
This code above is removing duplicates based on ID which I don't want and gives this output below:
id rank created_date
1 1 1/1/2014
2 2 1/1/2021
3 3 1/1/2021
Any help will go a long way thanks
you can use GROUP BY with max() function.
e.g. select * from table group by rank having max(created_date)
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'm trying to execute the following sqlite command to copy some values in a table:
INSERT OR IGNORE INTO table (column1, column2) VALUES((SELECT DISTINCT column1 FROM table WHERE column2 = '2'), 1);
What I want is for the 1 to be used for all the SELECTed rows, but it's only used once. How do I fix that?
I was thinking using a temp table with the default value to what I want. I know temp tables are used for complex queries, but it feels like this isn't that complicated and there could be an easier solution to this.
Example:
I want the following table...
column1 column2
1 2
4 2
5 3
4 4
...to become the following table...
column1 column2
1 2
4 2
5 3
4 4
1 1
4 1
Looks like you want to put the fixed column2 value in the select:
INSERT OR IGNORE INTO table (column1, column2) SELECT DISTINCT column1, 1 FROM table WHERE column2 = '2';
Also note the absence of VALUES.
I have the below table in my DB
id name is_current
1 apple 0
2 banana 1
3 mango 0
4 grapes 1
5 pineapple 1
I want to execute an update query which will update the (fruit) table last column (is_current) single value and at the same time the whole column values as well. For example the first row has an apple with id=1 and I want to set this value to 1 and all other fruit values to zero so the table look like,
id name is_current
1 apple 1
2 banana 0
3 mango 0
4 grapes 0
5 pineapple 0
Currently I am using two different queries and to different methods to achieve this
1st: is to set all values of is_current column to 0
String sql = "UPDATE "+TABLE_NAME +" SET " + is_current + " = '"+ Zero +"';
2nd: is to set the apple values to 1 by using id
String sql = "UPDATE "+TABLE_NAME +" SET " + is_current + " = '"+ One +"' WHERE "+ id + " = "+rowId;
So how can I combine these two queries to a single one to achieve this?
If you want to do this stuff for a specific fruit/ID (e.g. every time you update apples) you can set a trigger such that when you update that fruit, then it will automatically set to zero all other rows.
However if you want to do this stuff in a more general way then you need to perform 2 queries (as told by #Der Golem)
how can I combine these two queries to a single one to achieve this?
You can't.
You have to execute at least 2 commands.
A command to update all the is_current values to 0.
And a command to update the specified record to 1.
I have a SQLite table defined this way:
CREATE TABLE Points(value INTEGER, player INTEGER, match INTEGER)
In the execution, I may have several identical columns, and I want a call which only deletes one, not all of them nor keeping just one. Is there any SQL call to do that?
An example to explain myself clearer:
value player match
1 2 3
1 3 3
1 2 3
2 2 3
1 2 3
1 2 3
1 3 3
db.delete("Points", "value = 1, player = 2, match = 3", null); //pseudo-code for the deletion
db.delete("Points", "value = 1, player = 3, match = 3", null);
value player match
1 2 3
2 2 3
1 2 3
1 2 3
1 3 3
I think db.delete("Points", "value = 1, player = 3, match = 3", null); will delete ALL columns which match the where clauses, am I right?
The delete statement you wrote will indeed delete all matching rows.
Try to use the built-in column ROWID, read the first line, save the rowid, and then delete where ROWID= the value you selected.
Try this
String _val=""+1;
String _player=""+3;
String _match=""+3;
db.delete(TABLE_NAME,"value=? AND player=? AND match=?",new String[] {_val,_player,_match});
The usual way to do this is to assign every table a unique identifier for each row. Like this:
CREATE TABLE Points(Id INTEGER PRIMARY KEY, value INTEGER, player INTEGER, match INTEGER);
Then you can use a subquery to find the one row you want to delete:
DELETE FROM Points WHERE Id =(SELECT MIN(Id) FROM Points WHERE value=1 AND player=2 and match=3);
This example deletes the oldest record entered first (the oldest has the lowest unique key Id)