How to calculate running total without using windows function - android

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.

Related

Select unique Records based on a column by conditionally removing the other record having same column value based on some other coumn in sql

I am developing an Android app in which I am using the Room persistence library. In one of the Tables (Room Entity), I have a case where I want to select unique Records based on a column by conditionally removing the other record having the same column value based on some other column.
Consider the following table as a base.
S_ID
STATUS
1
Pending
1
Rejected
3
Approved
4
Approved
5
Pending
6
Rejected
7
Rejected
the expected result of the SQL query where I want a record with 'Rejected' status to be removed from results if its S_ID collides with another 'Pending' status record
S_ID
STATUS
1
Pending
3
Approved
4
Approved
5
Pending
6
Rejected
7
Rejected
what I have tried so far:
select DISTINCT s_id OR s_id = 0, * from TABLE1
I think You can try below query :
SELECT * FROM table1 WHERE table1.S_ID in (SELECT S_ID FROM table1 GROUP BY S_ID HAVING COUNT(S_ID)>0) and table1.[status]<>'Rejected'
Edited :
SELECT table1.S_ID,
(select Alias.[Status] from Table1 Alias where Alias.S_ID=table1.S_ID
and ((COUNT(table1.S_ID)>1 and Alias.[Status]<>'Rejected') or COUNT(table1.S_ID)=1)
) as [Status]
FROM table1 GROUP BY S_ID HAVING COUNT(S_ID)>0
You can use aggregation for this:
select s_id, min(status) as status
from table_id
group by s_id;
This uses the fact that alphabetical ordering is used for max() and 'Pending' < 'Rejecdted'.

SQL Query, How to find the first and last value in the given table need by sequential order with conditions

I have ids in my table, ids start from 1 to 20, I want a query, to find the first and last records in a given table but I want the result by some condition.
For example: if I have the record
1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
This is the island part of the classic gaps and islands problem (With the gaps part being finding the missing values in between each island). If you search for that term, you'll find a ton of material about how to calculate them.
One approach (Requires Sqlite 3.25 or newer for window function support):
sqlite> CREATE TABLE ex(id INTEGER PRIMARY KEY);
sqlite> INSERT INTO ex VALUES (1),(2),(3),(4),(5),(9),(10),(11),(12),(13),(19),(20);
sqlite> WITH cte AS (SELECT id, id - row_number() OVER (ORDER BY id) AS grp FROM ex)
...> SELECT min(id) AS rangestart, max(id) AS rangeend FROM cte GROUP BY grp;
rangestart rangeend
---------- ----------
1 5
9 13
19 20
SQL Query to find first record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> ASC LIMIT 1
SQL Query to find last record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> DESC LIMIT 1
For example: if I have the record 1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
If you need result like you have mentioned, then you can set LIMIT in your query to get how many records you can have in that query.
QUERY:
SELECT * FROM <table_name> LIMIT <any_number>

Update current cursor position record Sql

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.

Sqlite - query 1 column in two identical tables where column equals a value

I have two identical tables (month1, month2) and I am trying to find all records from both tables where task1_done = 1. I want the last row in that set (i move cursor to last for this). I have played with inner outer natural joins but can't seem to get month2 values. Here is what I have:
String query = "SELECT m1.columnA, m1.columnB, m1.columnC, m1.columnD, m1.columnE, m1.columnF FROM month1 m1, month2 m2 WHERE m1.task1_done = 1 OR m2.task1_done = 1";
Any help would be great!
I think you want a union all for this query:
select m.*
from (select *
from months1
union all
select *
from months2
) m
where task1_done = 1;
Note: I have used * as a convenience because you said the two tables have the same structure. You should actually list the columns that you want from the two tables.
In general, having two tables with the same layout is a sign of a bad database design. It is usually better to have a bigger table, with another column identifying "month1" or "month2".
EDIT:
SQL tables do not have a "last" value. If you have a an id or timestamp column that you can use for ordering, then you can do:
select m.*
from (select *
from months1
union all
select *
from months2
) m
where task1_done = 1
order by id desc
limit 1;
Are these tables related or have any references? if not you can have separate statement and do a union
i.e.
select top 1 column1, column2.. from month1 WHERE task1_done = 1 order by IdentityColumn Desc
union
select top 1 column1, column2.. from month2 WHERE task1_done = 1 order by IdentityColumn Desc

SQL sqlite conditional query in sqlite (for Android)

Hello I have a table with that structure:
ID VALUE
1 3
2 5
3 12
if I query select id,value,value+5 from table. Thats the result
ID VALUE NEW_VALUE
1 3 8
2 5 10
3 12 17
And what I want to make a query indicating the id and the new value that return the whole table but with a 3rd column indicating the new values after inserting. for example for myQuery(id=2,value=8)
ID VALUE NEW_VALUE
1 3 3
2 5 8
3 12 12
Is posible to do that in the same query?
YOu can use the WHERE clause to select only the rows you want ("...if the student has the given id..."):
update T
set col3 = col2 + 5
where id = 2
Of course, col3 would have to exist before you can update it. So you will either have to issue an ALTER-TABLE statement (if your implementation supports it) or recreate the table with the desired columns, import the original data (INSERT INTO YOURNEWTABLE...SELECT ... from YOUROLDTABLE) and then update col3.
If you don't want to "persist" this third column but only need it to be displayed when you query:
select id, col2, col2 + 5 as myComputedValue
from T
where id = 2
Finally, if you want to display all rows but change the addend conditionally (add zero to col2 when the id is not one of the ones you desire but add 5 when it is) then you can use the CASE statement.

Categories

Resources