Unnest (or similar) in SQLite - android

I'd like to return multiple rows as a single row to be handled by an Android Cursor Adapter.
I currently have a table with a date column and I'd like to return, as a single row, all the rows that have the same date.
Consider the following table:
ID | Name | Date
-------------------------------
1 | 'Mark' | '08/06/15'
2 | 'Peter' | '08/06/15'
3 | 'Henry' | '08/06/15'
4 | 'Bob' | '17/04/16'
5 | 'Tony' | '23/08/13'
6 | 'Tim' | '17/04/16'
I'd like to query the results as follows:
Date | Names
------------------------------------
'08/06/15' | 'Mark, Peter, Henry'
'17/04/16' | 'Bob, Tim'
'23/08/13' | 'Tony'
Using this link I was able to obtain the following query:
SELECT t1.id, GROUP_CONCAT(t1.Name ) AS Names
FROM Table1 t1 JOIN Table2 t2 ON t1.ID = t2.ID
GROUP BY t1.ID;
However, since all the data is from the same table, and I know the dates to query in advance, I was hoping to use JOIN with an inputted array and have SQLite parse it (ie. like unnest in Postgres) as if it came from an actual table.
The array would be something like:
['08/06/15', '17/04/16', '23/08/13', '09/08/18']
This can probably also be done by nesting SQL queries, but I'd like an optimized solution if possible.

SQLite has no array type.
The simplest way would be to use the IN operator:
SELECT ID, group_concat(Name) AS Names
FROM MyTable
WHERE Date IN ('08/06/15', '17/04/16', '23/08/13', '09/08/18')
GROUP BY ID;
If you really want to use a join, you can construct a virtual table with a VALUES clause (in Android Jelly Bean or later), or a compound query:
SELECT ID, group_concat(Name) AS Names
FROM MyTable
JOIN (VALUES('08/06/15'), ('17/04/16'), ('23/08/13'), ('09/08/18'))
ON Date = column1
GROUP BY ID;
SELECT ID, group_concat(Name) AS Names
FROM MyTable
JOIN (SELECT '08/06/15' AS Date UNION ALL
SELECT '17/04/16' UNION ALL
SELECT '23/08/13' UNION ALL
SELECT '09/08/18' ) USING (Date)
GROUP BY ID;

Is this what you want:
SELECT Date, group_concat(Name) AS Names
FROM table_name
GROUP BY Date;

Related

Android Studio - Room query different result than expected

I'm having a difficult time understanding why does the same query on the same database is correct on my local testing environment (my computer) and is wrong over my device/emulator.
The database is literally the same (copied from emulator to computer).
SELECT * FROM (
SELECT name, max(date_col) as date_col, value FROM tbl
WHERE date_col <= '2021-06-30'
GROUP BY name
UNION
SELECT name, min(date_col) as date_col, value FROM tbl
GROUP BY name
ORDER BY date_col DESC
)
GROUP BY name
The schema for the table is (id, date_col, name, value).
What I'm trying to do is select all the rows with the nearest date to the supplied date. I'm not quite sure that this is the best way of doing it so any suggestions are welcomed
When trying this query in my computer (SQLITE v3.27.2) it works as intended and when testing on a device/emulator (Tried multiple API levels such as 23, 27, 28) they all failed.
Edit:
Table data:
|name |date_col |value|
|-----|----------|-----|
|NAME6|2021-06-29|71 |
|NAME7|2021-06-29|80 |
|NAME1|2021-06-29|2925 |
|NAME4|2021-06-29|182.0|
|NAME2|2021-06-29|365 |
|NAME3|2021-06-29|81.0 |
|NAME5|2021-06-29|0.25 |
|NAME7|2021-06-27|81.0 |
|NAME1|2021-06-27|3000 |
|NAME5|2021-06-01|0.35 |
|NAME6|2021-06-01|68.0 |
|NAME5|2021-06-28|0.15 |
Results on device (for date 2021-06-28):
|name |date_col |value|
|-----|----------|-----|
|NAME1|2021-06-27|3000 |
|NAME2|2021-06-29|365.0|
|NAME3|2021-06-29|81.0 |
|NAME4|2021-06-29|182.0|
|NAME5|2021-06-01|0.35 |
|NAME6|2021-06-01|68.0 |
|NAME7|2021-06-27|81.0 |
Results on computer (for date 2021-06-28):
|name |date_col |value|
|-----|----------|-----|
|NAME1|2021-06-27|3000 |
|NAME2|2021-06-29|365.0|
|NAME3|2021-06-29|81.0 |
|NAME4|2021-06-29|182.0|
|NAME5|2021-06-28|0.15 |
|NAME6|2021-06-01|68.0 |
|NAME7|2021-06-27|81.0 |
As you can see, in this example the difference is with name5, it should be 0.15 but on device for some reason its 0.35.
What could be the reason for these differences?
Thank you very much!
SQLite allows statements with SELECT * and GROUP BY, but the resulting rows are arbitrary.
If you want to use this in lower API levels, you can't use window functions which would easily solve the problem like this:
SELECT name, date_col, value
FROM (
SELECT *, ROW_NUMBER() OVER (
PARTITION BY name
ORDER BY date_col < '2021-06-28' DESC,
abs(strftime('%s', date_col) - strftime('%s', '2021-06-28'))
) rn
FROM tbl
)
WHERE rn = 1
ORDER BY name;
You can use a correlated subquery:
SELECT t1.name, t1.date_col, t1.value
FROM tbl t1
WHERE t1.date_col = (
SELECT t2.date_col
FROM tbl t2
WHERE t2.name = t1.name
ORDER BY date_col < '2021-06-28' DESC,
abs(strftime('%s', t2.date_col) - strftime('%s', '2021-06-28'))
LIMIT 1
)
ORDER BY name;
See the demo.

Get Summation using in keyword in Sqlite Database Android

Need to get the total sum and count of the rows using join query while searching with tags ids.
Search can be done using multiple tag ids.I am trying to do it using 'in' keyword but it is returning wrong summation not able to understand how to get the summation while joining tables
Query 1:
SELECT SUM(amount) as Total,count(*) as count FROM
tbl_transactions
where trans_type='Expenses' and DATE(date) BETWEEN '2019-08-01' AND '2019-12-10'
AND trans_type='Expenses'
returns the right value as there is no joining
Query 2:
SELECT SUM(amount) as Total,count(*) as count FROM
tbl_transactions g
left join tbl_dummy2 d2
on d2.colA =g.trans_id
left join tbl_dummy d
on d.dummy_id1=d2.colB
where trans_type='Expenses' and d2.colB in (1,2) and DATE(date) BETWEEN '2019-08-01' AND '2019-12-10'
AND trans_type='Expenses'
returns the wrong value as joining is not properly done
I have three table:
tbl_transactions which holds all the transaction
tbl_dummy which holds all the tags (master table)
tbl_dummy2 which holds the colA(trans_id) and colB(tagids)
Move the condition d2.colB IN (1, 2) in the ON clause, because when you have it in the WHERE clause it will return only the matching rows of the LEFT JOIN which is actually an INNER JOIN:
SELECT SUM(t.amount) as Total, COUNT(*) as count
FROM tbl_transactions t
LEFT JOIN tbl_dummy2 d2 on d2.colA = t.trans_id
LEFT JOIN tbl_dummy d1 on d1.dummy_id1 = d2.colB AND d2.colB IN (1, 2)
WHERE t.trans_type='Expenses' AND DATE(t.date) BETWEEN '2019-08-01' AND '2019-12-10'
See the demo.
Results:
| Total | count |
| ----- | ----- |
| 8585 | 2 |

Querying records and making them groups from SQLite Table

I am not really understanding how can this be done in single query.
PROBLEM:
I have a table like this
id| phonenumber| message| group_name| datetime
Example data
1 | 987654321 | "hi" | G1 | xxxxxxx
2 | 987654321 | "hi" | G1 | xxxxxxx
1 | 987145678 | "hello" | G2 | xxxxxxx
What I want to do is query the above SqlLite table in such a way that I need to grab all the rows of particular phonenumber in Descending order of datetime. So that I can put them in my HashMap with key as group_name and value as ArrayList of messages.
HashMap<String, ArrayList<String>> mapper = new HashMap<>();
I am using GreenDao Library to fetch data from SqlLite, I tried like below
List<activity> activities = activityDao.queryBuilder().where(new WhereCondition.StringCondition(com.ficean.android.ficean.db.activityDao.Properties.Contact_number.eq(phonenumber) + "GROUP BY group_name")).orderDesc(com.ficean.android.ficean.db.activityDao.Properties.Date_time).build().list();
I managed to do above query using GROUP BY but it is not listing all rows.Do I have to get All data of particular number and separate it looping through each row based on group_name ? or is there any better way to do?
The SQL is quite simple, just a WHERE clause and an ORDER BY.
The SQL looks like this:
select t.*
from t
where t.phone = ?
order by t.datetime desc;
I am not sure how this translates into your SQL generator, but it does involve removing the GROUP BY.

Complex SQL query in Android SQLite

I have a table orders, consisting of 3 columns:
order_id int primary key,
cust_id integer,
order_date integer
with data:
order_id | cust_id | order_date
1 | 10 | 1325376000
2 | 10 | 1325548800
3 | 10 | 1325894400
4 | 11 | 1325462400
5 | 11 | 1325721600
6 | 12 | 1325721600
7 | 12 | 1326326400
I'm trying to write a query to give a Cursor containing the most recent order for a given customer that I can then pass to a SimpleCursorAdapter and bind to a ListView, such that the user sees the following:
10 1325894400 (formatted as human readable date)
11 1325721600
12 1326326400
I've tried joining the table to itself in various ways without any luck:
http://sqlfiddle.com/#!2/77b22d/1/0
If I have to populate an ArrayList and use an ArrayAdapter I will, but I'd like to exhaust this option first. Thanks!
EDIT: Apologize for the differences between here and the SQLFiddle, brain running on two separate threads. The Fiddle is the 'correct' data set.
2nd EDIT: Added a new wrinkle (ignore table above, see the SQL fiddle). Adding a field for free-form text and then running the query returns the first record in the GROUP BY, plus the field for the max_date. I need to pull the whole record containing the date that equals max_date. Adding a WHERE clause breaks the query. Thoughts?
Try this
select
order_number
, cust_number
, order_date
from orders o1
where order_number =
(
select order_number
from orders o2
where o2.cust_number = o1.cust_number
and order_date =
(
select max(order_date)
from orders o3
where o3.cust_number = o2.cust_number
)
)
This will get you the correct records and you can format the date as you like in the main query.
Note: My answer is a bit different form your display since the example here and the Fiddle are different. used the Fiddle one
create table orders (order_number integer primary key,
cust_number integer not null,
order_date integer not null);
insert into orders values (1001,10,1005),
(1,10,1325376000),
(2,10,1325548800),
(3,11,1325894400),
(4,11,1325462400),
(5,11,1325721600),
(6,12,1325721600),
(7,12,1326326400),
(8,12,1326326460);
If you just want the latest record for each customer, I think this will work:
SELECT order_number, cust_number, max(order_date) as max_date FROM orders GROUP BY cust_number
The values you put on the link are different from the ones you posted here but you are looking for:
select o1.cust_number, max(o1.order_date)
from orders o1
group by o1.cust_number
order by o1.cust_number ASC
This will give you for each customer the most recent order.

How to run DISTINCT on non-key in SQL

I have a database that can have similar rows, but have different keys and a different boolean column. Here is what the database looks like:
columns: _id, name, img, address, available
Two entries can look like this:
_id | name | img | address | available
-------------------------------------------------------
1 | John | http://img.com/222 | 1 Main St | 1
2 | John | http://img.com/222 | 1 Main St | 0
I want a query that will give me all results that have a distinct key, and if there are duplicate entries(ignoring the fact that _id would be different), it will give back only the first one. Here is the query I have:
SELECT p1.*
FROM (SELECT DISTINCT _id, available FROM people) p
INNER JOIN people p1
ON p1._id=p._id
ORDER BY p1.available DESC;
I know this isn't right, but maybe it explains a little what I am looking for. Would I want to use GROUP BY here?
I want a query that will give me all results that have a distinct key, and if there are duplicate entries(ignoring the fact that _id would be different), it will give back only the first one.....the _id isn't what I want to be distinct, as they [the ids] are already unique. ... . Ideally it will order by 'available' in descending order so that if there are two columns with the same data(aside from _id and available), it will return the row with '1' for the available column
select name, image, address, max(availability) as avail
from T
group by name, image, address
Then you can join the set returned by the query above, as an inline view, to your table:
select * from T
inner join
(
select name, image, address, max(availability) avail
from T
group by name, image, address
) as foo
on T.name = foo.name and T.image = foo.image and T.address = foo.address and T.availability = foo.avail
It would help to have a composite index so: (name, image, address).
Caveat: if there is more than one row where a specific {name, image, address} triad has availablility =1, the query will return multiple rows for the triad:
2 | John | http://img.com/222 | 1 Main St | 1
6 | John | http://img.com/222 | 1 Main St | 1
P.S. It sounds as though you wished the triad (name, image, address) had been created in your table an alternate UNIQUE key.
this sql may solve your problem:
select b.* from (select distinct _id from people) a, people b where a._id = b._id order by b.available
I actually just asked a similar question and received a great answer from an experienced user here:
SQL Populating with distinct data and a sequence
Based on what he told me, perhaps this query would provide you with what you want:
SELECT p1.*
FROM (SELECT DISTINCT _id, name from people) p
INNER JOIN people p1
ON p1._id=p._id
ORDER BY p1.available desc
apologies if that's a fail and doesn't work!
EDIT: It just occurred to me that I have no idea which distinct name+_id combo this will extract.. the available=1 or the available=0 or a random selection..! Let me know what happens anyway..
If you want the first row which has the lowest _id among those that have the highest available value (between 1 and 0), you can "record" the _id inside the aggregated value generated by the grouping.
The value to compare is constructed in a way that orders the record by their available field in descending order and then by their _id field in descending order, and allow to easily retrieve the value of the _id with the modulo operator (assuming available max value is 1 and the ids are never above 100000000).
select people.* from people
inner join (
select name, img, address,
min((1-available)*100000000 + _id) avail_id
from people group by name, img, address
) as foo on people._id = foo.avail_id % 100000000;
I adapted it Tim's query.
You can also do that without subquery:
select people.* from people
left outer join people as other on
other.name = people.name and
other.img = people.img and
people.address=other.address and
(1 - people.available) * 100000000 + people._id >
(1 - other.available) * 100000000 + other._id
where other.available is null;

Categories

Resources