DISTINCT with RANDOM() sorting - android

I'm creating Android countries quiz based on built-in SQLite. One of the task is to choose correct continent (from 4 options) for given country. I use the following DB structure:
continents: ID | name
countries: ID | continent (foreign key) | name | capital
App choose 4 random rows and return Cursor this way:
return db.rawQuery("SELECT country.name, continent.name FROM "+COUNTRIES_TABLE_NAME+" country LEFT JOIN "+CONTINENTS_TABLE_NAME+" continent ON country.continent = continent.id ORDER BY RANDOM() LIMIT 4", null);
What is the problem: SQLite can random countries that are from the same continent, then I have two or more equal answers. I wanted to avoid this problem adding DISTINCT:
SELECT country.name, DISTINCT continent.name...
Then I got an error (because of syntax, probably). What do I have to do then?

Maybe not the most beautiful SQL, but this should do it using one SELECT to get 4 distinct contintents, and another to get a random country in each;
SELECT
(SELECT name FROM countries c WHERE c.continent=con.id
ORDER BY RANDOM() LIMIT 1) name,
con.name
FROM (SELECT id,name FROM continents ORDER BY RANDOM() LIMIT 4) con
SQLFiddle here (SQLfiddle has a problem with calling RANDOM in SQLite WebSQL, so don't let it switch)

Try:
"SELECT DISTINCT country.name, continent.name
FROM "+COUNTRIES_TABLE_NAME+" country
LEFT JOIN "+CONTINENTS_TABLE_NAME+" continent ON country.continent = continent.id
ORDER BY RANDOM() LIMIT 4"

If you want distinct country and continent pair put distinct before country.name
See #Joachim Isaksson answer - it works!

Related

SQLite INNER JOIN - How to output only one of the common columns the two tables have

Let's say that I have two tables:
lessons with columns: lessonID, name
studentlessons with columns: lessonID, age
I am trying to perform this action:
SELECT lessons.*, studentlessons.* FROM studentlessons
JOIN lessons WHERE studentlessons.lessonID = lessons.lessonID
but the result is a table which have the columns:
lessonID, name, lessonID(1), age
I want to avoid the lessonID(1), so my desired output must be:
lessonID, name, age
I know that I can use this syntax:
SELECT lessons.lessonID, lessons.name, studentlessons.age FROM studentlessons
JOIN lessons WHERE studentlessons.lessonID = lessons.lessonID
but I can't because of some other reasons.
Is there any purely SQLite syntax that can give me my desired output?
What you want is a NATURAL JOIN:
SELECT * FROM studentlessons NATURAL JOIN lessons
which returns only one of the columns lessonID.
The tables are joined implicitly on the columns that have the same name(s).
See the demo.

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>

SQL Query for timestamp

I had following Table
CREATE TABLE Customer
( `Name` varchar(7), `Address` varchar(55), `City` varchar(15),`Contact` int,`timestamp` int)
;
INSERT INTO Customer
(`Name`,`Address`, `City`, `Contact`,`timestamp`)
VALUES
('Jack','New City','LA',79878458,456125),
('Joseph','New Lane23','LA',87458458,794865),
('Rosy','Old City','Paris',79878458,215125),
('Maria','New City','LA',79878458,699125),
('Jack','New City','LA',79878458,456125),
('Rosy','Old City','Paris',79878458,845125),
('Jack','New Main Street','New York',79878458,555525),
('Joseph','Near Bank','SAn Francisco',79878458,984521)
;
I want to get all customer record with highest timestamp without duplication.
Try the following.
select name,max(timestamp),Address,City,Contact from Customer group by name
I want to get all customer record with highest timestamp without
duplication.
Use DISTINCT operator and ORDER BY clause like
select distinct `Name`,`Address`, `City`, `Contact`,`timestamp`
from customer
order by `timestamp` desc;
In that case you can use JOIN query like
select t1.*
from customer t1 join
(select Name, max(`timestamp`) as maxstamp
from customer
group by Name) xx
on t1.Name = xx.Name
and t1.`timestamp` = xx.maxstamp;
Try this:
SELECT * FROM `customer`
group by name,Address,City,Contact,timestamp
order by timestamp desc
I'm joining the Customer table with itself, the condition c1.timestamp<c2.timestamp on the join clause combined with c2.timestamp IS NULL
will make sure that only the latest record for each person is returned. I put DISTINCT because on your sample data there are two records for Jack with the same timestamp:
SELECT DISTINCT
c1.*
FROM
Customer c1 LEFT JOIN Customer c2
ON c1.Name=c2.Name
AND c1.Contact=c2.Contact -- you might want to remove this
AND c1.timestamp<c2.timestamp
WHERE
c2.timestamp IS NULL
ORDER BY
Name, Address
Please see a fiddle here.

Multiple count() with WHERE clause

I have an SQL table which contains flashcard objects. The table has a column indicated whether a flashcard is free or paid. Flashcards are divided into categories. In my android app I need to display the number of flashcards which are free, and also the number of flashcards which are paid, for each category. If a flashcard isn't free, it is paid.
My SQL isn't great, so far I have a query which returns the number of flashcards which are free:
SELECT _id, category_primary, count(category_primary) FROM Flashcards WHERE available = '1' GROUP BY category_primary;
I want to try to get the count of both free and paid flashcards in a single query/cursor as I display the result in a ListView using an adapter.
You can add the available column to the GROUP BY:
SELECT
_id,
category_primary,
available,
count(category_primary)
FROM
Flashcards
GROUP BY
available,
category_primary;
As an aside, I would have expected you to need the _id column in both your original query and this updated version - I have left it out because I'm assuming your original query works fine.
select f.category_primary, count(f1._id)as available_count, count(f0._id)as disable_count
from (select _id, category_primary from Flashcards) f
left join (select _id from Flashcards where available='1') f1 on f1._id=f._id
left join (select _id from Flashcards where available='0') f0 on f0._id=f._id
group by f.category_primary
Steven Fenton's answer came close, but wasn't quite what I wanted. I found my answer here: How to get multiple counts with one SQL query?.
The query that worked for me is:
SELECT
_id,
category_primary,
sum(CASE WHEN Flashcards.available = '1' THEN 1 ELSE 0 END) category_free,
sum(CASE WHEN Flashcards.available = '0' THEN 1 ELSE 0 END) category_paid
FROM
Flashcards
GROUP BY
category_primary;
The naming of the column available is pretty poor on my part, and I didn't phrase my question very clearly. A better name for the column would be is_free Thanks for those who helped answer my question!

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