android sqlite select avg multiple columns - android

I have a problem with a query of sqlite. I want to do is the average of two columns and place the result in a new column. example:
id | max_ma | max_ta | avg_max (new column)
1 | 100 | 102 | 101 ==> (100+102)/2
2 | 100 | null | 100 ==> (100+0)/1 INGNORE NULL
wrong code:
public Cursor list() {
String lista_ge = "SELECT *, AVG(tr_max_m + tr_max_t) AS media_max FROM bdt_registro ORDER BY tr_fecha DESC, _id DESC";
return db.rawQuery(lista_ge, null);
}
or
public Cursor list() {
String lista_ge = "SELECT *, ((tr_max_m + tr_max_t)/COUNT(*)) AS media_max FROM bdt_registro ORDER BY tr_fecha DESC, _id DESC";
return db.rawQuery(lista_ge, null);
}
Thanks for your interest

Use the COALESCE function:
SELECT *,
(COALESCE(tr_max_m, tr_max_t, 0) + COALESCE(tr_max_t, tr_max_m, 0)) / 2 AS media_max
FROM
bdt_registro ORDER BY tr_fecha DESC, _id DESC
If both are non_NULL you get: (tr_max_t + tr_max_m) / 2
If tr_max_m is NULL you get: (tr_max_t + tr_max_t) / 2 = tr_max_t
If tr_max_t is NULL you get: (tr_max_m + tr_max_m) / 2 = tr_max_m
If both are NULL you get: (0 + 0) / 2 = 0

Probably the best approach will be to use a lot of cases:
SELECT max_ma, max_ta,
CASE WHEN max_ma IS NULL THEN
CASE WHEN max_ta IS NULL THEN 0
ELSE max_ta END
ELSE
CASE WHEN max_ta IS NULL THEN max_ma
ELSE (max_ma + max_ta) / 2 END
END avg_max
FROM user_address
Fiddle here.
By the way, I noticed you were trying to use AVG. If you're wondering how you could have used that function then this will give you an idea. It won't be faster than the previous approach because it adds more calculation, though:
SELECT id,
max(CASE WHEN kind = 1 THEN aMax END) max_ma,
max(CASE WHEN kind = 2 THEN aMax END) max_ta,
avg(aMax) aMax
FROM (
SELECT id, max_ma aMax, 1 kind FROM user_address
UNION ALL
SELECT id, max_ta, 2 FROM user_address
) s
GROUP BY id

Another solution based on function IFNULL and...
... "un-aggregated" aggregated function COUNT:
SELECT *,
(IFNULL(max_ma, 0)+IFNULL(max_ta, 0))/(COUNT(max_ma)+COUNT(max_ta)) AS avg_max
FROM bdt_registro
GROUP BY id;
... CASE:
SELECT *,
(IFNULL(max_ma, 0)+IFNULL(max_ta, 0)) / (CASE WHEN max_ma IS NULL THEN 0 ELSE 1 END + CASE WHEN max_ta IS NULL THEN 0 ELSE 1 END) AS avg_max
FROM bdt_registro;
ยป SQL Fiddle

Related

SQLite - How to use ROW_NUMBER() OVER

I wanted to get information about streaks of certain challenges from my database. I experimented with all of my queries in sqliteonline.com and therefore I found a query which meets my requirements to get these information:
select challengeid, min(date) as min_date, max(date) as max_date, count(*) as length from (select t.*, row_number() over (partition by challengeid order by date) as seqnum from ACTIVE_CHALLENGES_TABLE t) t group by challengeid, date(date, '-' || seqnum || ' day') HAVING challengeid = 4 AND value != 0
Everything works fine on sqliteonline.com, but when it comes to Android Studio I get following error:
android.database.sqlite.SQLiteException: near "(": syntax error (code 1): , while compiling: select challengeid, min(date) as min_date, max(date) as max_date, count() as length from (select t., row_number() over (partition by challengeid order by date) as seqnum from ACTIVE_CHALLENGES_TABLE t) t group by challengeid, date(date, '-' || seqnum || ' day') HAVING challengeid = 4 AND value != 0
I already tried finding out what causes the error. Throughout testing I found out that it could be the use of this snippet: SELECT row_number() OVER (challengeid) seqnum from ACTIVE_CHALLENGES_TABLE
I don't know what I should do. Can you please help me?
If your version of SQLite does not support window functions you can use a correlated subquery to get the column seqnum:
select challengeid, min(date) as min_date, max(date) as max_date, count(*) as length
from (
select t.*,
(
select count(*) + 1 from ACTIVE_CHALLENGES_TABLE
where challengeid = t.challengeid
and (date < t.date or (date = t.date and rowid < t.rowid))
) as seqnum
from ACTIVE_CHALLENGES_TABLE t
where t.challengeid = 4 and t.value != 0
)
group by challengeid, date(date, '-' || seqnum || ' day')
Note that I removed the HAVING clause, which does not make sense since it does not filter out any aggregated columns and I replaced it with a WHERE clause inside the subquery, since you want results only under the condition:
challengeid = 4 and value != 0

SQLITE: How to get One row from table + get rest of rows in different order

I have a table with below rows.
rowid type value
1 A 13
2 A 14
3 B 12
4 B 15
5 C 17
6 C 16
I want to get the bigest value of type A first, it's rowid=2, then others in order by value DESC, the rowids are 56413
How to implement it sqlite? Thanks a lot!
If you wanted to do this only in the order by:
order by (case when type = 'A' and
value = (select max(t2.value) from t t2 where t2.type = 'A')
then 1 else 2
end),
value desc;
Or:
select t.*
from t cross join
(select max(value) as maxvalue from t where type = 'A'
) ta
order by (case when t.type = 'A' and t.value = ta.maxvalue then 1 else 2 end),
value desc;
Simply use the UNION clause to join 2 queries in one.
SELECT Type, Value FROM YourTableName WHERE rowID = 2
UNION
SELECT Type, Value FROM YourTableName WHERE rowID != 2 ORDER BY value DESC

Android Content Provider how to query with multiple selectionArg

I have table like below:
Students:
id | student_name
------------------
1 | max
2 | alex
3 | james
I want to query only students which have id 1 and 3.
getContentResolver().query(StudentContentProvider.CONTENT_URI2, projection,"id=?",selectionArg,null);
what I have to write in selectionArg so i only get students with id 1 and 3 ?
have you tried
getContentResolver().query(StudentContentProvider.CONTENT_URI2, projection,"id=1 or id=3",null,null);
or if you use placeholders "?" selectionArg must contain values "1" and "3"
getContentResolver().query(StudentContentProvider.CONTENT_URI2, projection,"id=? or id=?",selectionArg,null);
The answer i found is to add id in (?,?) for "selection" and give "selectionArgs[]" to query. ? signs replaced with elements inside "selectionArgs[]". You have to add ? for each element so i did this:
String selection1 = DatabaseOpenHelper.COLUMN_ID + " in (";
for (int i = 0; i < selectionArgs1.length; i++) {
selection1 += "?, ";}
selection1 = selection1.substring(0, selection1.length() - 2) + ")";
getContentResolver().query(StudentContentProvider.CONTENT_URI1, projection1, selection1, selectionArgs1, null);

select query - each two row into single result row

Lets assume I have the following 'Products' table:
ProductID | ProductName
----------+---------
0255463 | ProductA
0254483 | ProductB
0255341 | ProductC
0905454 | ProductD
Is there a way (in Android's SQLite) to select each two consecutive rows into single result row?
Here is the desired query result:
FirstProductID | FirstProductName | SecondProductID | SecondProductName
---------------+------------------+-----------------+---------
0255463 | ProductA | 0254483 | ProductB
0255341 | ProductC | 0905454 | ProductD
I would like a generic solution that can be used to any table, regardless the table content.
Create a temporary table with an auto increment column
CREATE TEMP TABLE temp(
id int not null primary auto increment,
pid int,
pname text,
);
Insert select this data into the temporary table
INSERT INTO temp (pid, pname) SELECT * FROM Products;
Join the temporary table on id = id + 1 where the first instance has id % 2 = 0
SELECT t1.pid AS t1_pid, t1.pname AS t1_pname,
t2.pid AS t2_pid, t2.pname AS t2_pname
FROM temp as t1 LEFT JOIN temp AS t2
ON t1.id + 1 = t2.id
WHERE t1.id % 2 = 0;
Single query (not faster):
SELECT
First.ProductId AS FirstProductId,
First.ProductName AS FirstProductName,
Second.ProductId AS SecondProductId,
Second.ProductName AS SecondProductName
FROM
(SELECT *, Cnt/2 AS Line FROM (
SELECT *, (
SELECT COUNT() FROM Products AS _ WHERE ROWID<Products.ROWID
) AS Cnt FROM Products WHERE Cnt%2=0
)) AS First
LEFT JOIN
(SELECT *, Cnt/2 AS Line FROM (
SELECT *, (
SELECT COUNT() FROM Products AS _ WHERE ROWID<Products.ROWID
) AS Cnt FROM Products WHERE Cnt%2=1
)) AS Second
ON First.Line = Second.Line
ORDER BY First.Line;
If you need a faster solution, #kzarns proposed a good one.
It is easy if you have consecutive IDs for the rows:
SELECT t1.productID AS productID1, t1.ProductName AS productName1, t2.productID AS productID2, t2.ProductName AS ProductName2
FROM product t1
JOIN product t2 WHERE t1.id+1 = t2.id
WHERE MOD(t1.id,2) = 1
ORDER BY t1.id
this is the sample idea , basically what am i doing is obtain different result from 2 select statement and join it , have a try on it, cheer =)
--sample table
DECLARE #SAMPLE TABLE
(
ProductID INT,
ProductName NVARCHAR(255)
)
--sample data
INSERT INTO #SAMPLE
VALUES
('1','ProductA'),
('2','ProductB'),
('3','ProductC'),
('4','ProductD')
SELECT FirstProductID,FirstProductName,SecondProductID,SecondProductName FROM
(
--Query to get firstProduct items
SELECT ROW_NUMBER() OVER (ORDER BY FirstProductID) firstrn,FirstProductID,FirstProductName
FROM(
SELECT ProductID 'FirstProductID',ProductName 'FirstProductName' FROM
(
SELECT ROW_NUMBER() OVER (Order by productid) firstrn1,ProductID,ProductName
FROM #SAMPLE
) FIRSTPRODUCTTABLE
WHERE firstrn1%2 = 1
) FIRSTPRODUCTTABLE1
)t1
-A join is performed
LEFT OUTER JOIN (
--Query to get second product items
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY SecondProductID) rownumber,SecondProductID,SecondProductName
FROM(
SELECT ProductID 'SecondProductID',ProductName 'SecondProductName' FROM
(
SELECT ROW_NUMBER() OVER (Order by productid) rn,ProductID,ProductName
FROM #SAMPLE
) SECONDPRODUCTTABLE
WHERE rn%2 = 0
)SECONDPRODUCTTABLE1
) t2
) t3 ON t1.firstrn=t3.rownumber
Doing this in SQL would be rather complex and slow.
The cleanest way to reorder column values like this would be to implement your own Cursor class that wraps the original database cursor, but doubles the number of columns, and redirects all column accesses to the appropriate record.
Something like this:
class DoubleColumnCursor implements Cursor {
Cursor baseCursor;
int baseColumns;
int currentPosition;
public getColumnCount() {
return baseColumns * 2;
}
public String getColumnName(int columnIndex) {
if (columnIndex < baseColumns)
return baseCursor.getColumnName(columnIndex) + "1";
else
return baseCursor.getColumnName(columnIndex - baseColumns) + "2";
}
public boolean moveToPosition(int position) {
boolean result = baseCursor.moveToPosition(position * 2);
if (result)
currentPosition = position;
return result;
}
public String getString(int columnIndex) {
if (columnIndex < baseColumns) {
baseCursor.moveToPosition(currentPosition * 2);
return baseCursor.getString(columnIndex);
} else {
baseCursor.moveToPosition(currentPosition * 2 + 1);
return baseCursor.getString(columnIndex - baseColumns);
}
}
...
};

What is the best way to do SELECT in SQLite with equals subqueries

I do big query with few subqueries and unions:
public Cursor getCursor(int outlayType, long carId){
String selection;
String[] selectionArgs;
if(outlayType>0){
selection = "car_id=? and type=?";
selectionArgs = new String[]{
Long.toString(carId),Integer.toString(outlayType),
Long.toString(carId),Integer.toString(outlayType),
Long.toString(carId),Integer.toString(outlayType)};
}else{
selection = "car_id=?";
selectionArgs = new String[]{Long.toString(carId), Long.toString(carId), Long.toString(carId)};
}
String sql = "select (select count(*)+1 from outlays b where a.date < b.date and "+selection+") as countNum," +
" id as _id, id, type, note, sum, date," +
" odometer, unread, future, input_type, 0 as row_type " +
" from outlays a where "+ selection +" " +
" union " +
" select 0, 0, 0, 0, '', sum(sum), max(date)+2," +
" 0, 0, 0, '', 1 as row_type" +
" from outlays where "+ selection +" group by strftime('%Y-%m', date/1000, 'unixepoch')" +
" order by 7 DESC";
return sqdb.rawQuery(sql, selectionArgs);
}
It works perfect, but ...
You can see: few times I use same WHERE conditions. And this is not the end. The query will grow. And this conditions will be use again and again.
I want to use temp table like this:
public Cursor getCursor(int outlayType, long carId){
String selection;
String[] selectionArgs;
if(outlayType>0){
selection = "car_id=? and type=?";
selectionArgs = new String[]{Long.toString(carId),Integer.toString(outlayType)};
}else{
selection = "car_id=?";
selectionArgs = new String[]{Long.toString(carId)};
}
sqdb.execSQL("drop table if exists sub");
sqdb.execSQL("create temp table sub " +
"as select * from outlays where "+selection, selectionArgs);
String sql = "select (select count(*)+1 from sub b where a.date < b.date) as countNum," +
" id as _id, id, type, note, sum, date," +
" odometer, unread, future, input_type, 0 as row_type " +
" from sub a " +
" union " +
" select 0, 0, 0, 0, '', sum(sum), max(date)+2," +
" 0, 0, 0, '', 1 as row_type" +
" from sub group by strftime('%Y-%m', date/1000, 'unixepoch')" +
" " +
" order by 7 DESC";
return sqdb.rawQuery(sql, null);
}
It looks better (for me), but when I call Cursor.notifyDataSetChanged - it works wrong. Because the recreation of temp table is not called.
How can I do one subquery or one temp table in the same query for Cursor?
I not sure what your ultimate goal is, but I was able to convert your first query into a correlated query. That is where the subselect gets if values for a column from the main query. The key is that the main query column MUST always be on the right side of the comparison operator in the subselect, so I reversed the dates and switch the comparison operator.
Using the following data as input:
1 1 1 note 1 1 20130601 6100 1 1 0 201306
2 2 2 note 2 2 20130701 72013 0 1 0 201307
3 3 3 note 3 3 20130715 4201 1 1 a 0 201307
4 3 3 note 4 4 20130318 68010 1 1 0 201303
5 1 1 note 5 5 20130615 37077 1 1 0 201306
I couldn't replicate the strftime with the tool is was using so I added the last column ym, which I believe is equivalent to your result of strftime for your group by.
Here's the SQL i came up with:
select (select count(*)+1 from outlays b where b.date1 > a.date1 and b.car_id=a.car_id) as countNum,
id as _id, a.id, a.type, a.note, a.sum1, a.date1, a.odometer, a.unread, a.future, a.input_type, 0 as row_type, ym
from outlays a where car_id= 1
union all
select 0, 0, 0, 0, '', sum(sum1), max(date1)+2, 0, 0, 0, ' ', 1 as row_type, ym from outlays group by ym order by 7 DESC;
There are the data results:
#| |.|.|.|. |.|. |. |.|.|.|.|.
-+-+-+-+-+------+-+--------+-----+-+-+-+-+------
1|0|0|0|0| |5|20130717|0 |0|0| |1|201307
2|0|0|0|0| |6|20130617|0 |0|0| |1|201306
3|1|5|5|1|note 5|5|20130615|37077|1|1| |0|201306
4|2|1|1|1|note 1|1|20130601|6100 |1|1| |0|201306
5|0|0|0|0| |4|20130320|0 |0|0| |1|201303
Note in this particular case until I really know the goal, either UNION or UNION ALL can be used.
Some things I noted and changed a few in the above:
There probably isn't a column name issue here 'id as _id and id' with the exception that you are almost being reduntant, in the future just use _id. Anyone maintaining the code will then understand it a bit quicker. Nice way to get around the _id requirement by android methods.
There is also an issue probably with a column names of 'sum', and 'date' since it is also a sql function or might be, or a reserved word in some language and if not, I would just avoid out of good database design.
FYI: Not that this one matters at all, but type isn't really descriptive, column names should be descriptive, so type should be type_ofwhat, but that only if you want to follow good database design.
In addition, since I didn't know your intent, I wasn't sure there were other columns that you would want to group by. From my experience, typically there are.
The two queries might be able to be one, again I'd need to know what the real intent is for the result and all columns therein.
Hope this helps.

Categories

Resources