If table Scores looks like this:
_id | score
---------
1 | 1,000
2 | 2,000
3 | 3,000
4 | 4,000
5 | -1
6 | -1
7 | -1
Will the following query always return the rows in _id ascending order?
SELECT * FROM Scores
Also, will the following query always return the first ordered occurrence of _id (that is, 5)?
SELECT _id FROM Scores WHERE Score = -1 LIMIT 0, 1
The key here is ALWAYS. I have tested it and it works as intended but I want to verify this outcome is guaranteed and that an order by clause is not needed in these cases.
Thank you all.
By default, the rows are logically stored in order of increasing rowid.
And if your select stament does not include an order by, or the table has no index, the default sorting order will always be the primary key column, which is the _ID column in this case
Read more here
One of the principles of the databases is that you can not suppose that your registers are ordered in any way, as the internal implementation of SQLite may differ between devices. You should use the operator order by to assure a certain order.
Related
I visited sqlfiddle.com and made some tests.
I wanted to get a SQL statement that would let me insert a row, and have a field (below is called active) whose value depends on the table being empty or not.
I used the following schema:
CREATE TABLE kids (
identifier INTEGER PRIMARY KEY,
name VARCHAR,
surname VARCHAR,
active INTEGER);
INSERT INTO
kids
VALUES
(1, "name1", "surname1", CASE WHEN EXISTS (SELECT * FROM kids) THEN 0 ELSE 1 END);
INSERT INTO
kids
VALUES
(2, "name2", "surname2", CASE WHEN EXISTS (SELECT * FROM kids) THEN 0 ELSE 1 END);
And I get the following result when performing a SELECT * FROM kids:
+---+-------+----------+---+
| 1 | name1 | surname1 | 1 |
+---+-------+----------+---+
| 2 | name2 | surname2 | 0 |
+---+-------+----------+---+
This is perfect. The thing is, how do I perform this insert in Android using SQLite and ContentProviders?. Both insert (on ContentProviders and SQLite instance take a ContentValues as a parameter, and I can't add a subquery there, it would be literally read and written.
Is it even possible to achieve this ? If you can think of any other approach, it is also welcome.
Thank you.
Another way of achieving your requirement would be to check the value of underlying field before insertion and then insert the entire data along with computed value in active field
ContentValues ensures that it contents are never interpreted as SQL, so this is not possible.
Unless you actually need to provide data to other apps, do not use content providers. (And if you need to give data to other apps, it would be a bad idea to allow them to execute arbitrary SQL.)
I am trying to build a quiz app and the entries in the database look like this
id | term | synonym 1 | synonym 1 answered | synonym 2 | synonym 2 answered | ....
----------------------------------------------------------------------------------
1 | read | recite | Yes | study | No |
2 | work | effort | No | labour | Yes |
The idea is to present one synonym at a time. Once the next synonym is chosen, the previous synonym is marked as answered using the column next to the synonym with "Yes"
For the logic to select a word I am using a Collection.shuffle() function to get a random row and random column, query database to see if its answered column is "No". If "Yes" I am repeating the shuffle till I get a "No".
For knowing if atleast one entry in the entire table has a "No" in any of the 'answered' columns, I am using an OR clause against the answered columns (to make sure that all synonyms are not already answered)
So my app is doing lot of iterations to get the desired word which is definitely very bad way.
I am unable to figure a way to let sqlite query return me a random row and column that has the word "No". If I can get the column name of the result that had "No", I can strip the word 'answered' and get the related synonym column in a row, and present it with out much of java code.
Can any one kindly enlighten me on this and give a solution? I would require the column name and the rowid of the resulting match of a word "No" in the entire table. And it must be at RANDOM
Edit:
The scenario given here is for simplicity. The actual app deals with Sanskrit grammar and requires the kind of implementation I am planning. I require to get the 'name' of the column and 'id' of the row that got a 'No'. In the implementation I won't be using 'Yes' and 'No'. The number of columns would be fixed for all the terms. For simplicity I gave this example.
I would recommend the following:
First normalize your table structure. There will be two tables: Terms and Synonyms
Terms table will have Id and TermName
In the Synonyms table there will be SynonymId, Name, Answered TermId So, you can track synonyms through TermId here.
This way, you can easily query to see what synonyms are still not answered for a specific term like:
SELECT * FROM Synomyms WHERE TermId = 1 AND Answered = "No"
Hope this helps
Well, the yes/no part is a little tedious, especially if you did it that way (to me, another relational table would make more sense, but that's another story).
SELECT *
FROM table
WHERE
synonym_1_answered = 'No'
OR synonym_2_answered = 'No'
OR ...
ORDER BY RANDOM() LIMIT 1;
Filter the ones with a "No" using where, and use the random function to "sort" (more like unsort) them.
Edit:
Once you're done with the data selection, and provided you have a Cursor pointing to this result, it's as easy as:
Long long = cursor.getLong(cursor.getColumnIndex("long"));
boolean is_synonym_1_answered =
cursor.getString(
cursor.getColumnIndex("synonym_1_answered")
).equals('Yes');
boolean is_synonym_2_answered =
cursor.getString(
cursor.getColumnIndex("synonym_2_answered")
).equals('Yes');
...
After that, you only need to check which of the booleans is negative, and thus that synonym hasn't been answered yet. You can speed up things by storing the positions of your query in constants (so ID is 0, TERM is 1... The typical situation is to store them in an Array). That way you don't waste time asking the cursor for the position of a column:
Long long = cursor.getLong(ID);
boolean is_synonym_1_answered = cursor.getString(SYNONYM_1_ANS).equals('Yes');
boolean is_synonym_2_answered = cursor.getString(SYNONYM_2_ANS).equals('Yes');
...
Again, to speed up things, a second table containing just a synonym and a reference to the original term would make things way easier (as every row returned by a query like that would be an unchecked synonym).
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.
I'm querying a database in Android. The table is your ordinary table with values, nothing special.
What I need: return the two events that happened before and after the given timestamp.
Example: let's suppose I have the table below, and my selection is 1332200003002:
_id | Time | Value
... ...
n | 1332200002000 | 145
n+1 | 1332200003001 | 98 (this is <= selection)
(1332200003002 is here, between those two)
n+2 | 1332200004000 | 90 (this is > selection)
n+3 | 1332200005000 | 100
n+4 | 1332200005001 | 280
So, if my selection is 1332200003001, or 1332200003002... I'd want the returned rows to be n+1 and n+2, so that I can see that the Value went from 98 to 90.
What I'm using is a CursorLoader, so it must preferably fit into its usual call.
My code size thanks you!
As a side note, I can guess safe values for BETWEEN (it IS working already), and then iterate the few remaining Cursor rows in Java to pinpoint the two rows that I need. However, this seems to me like a very common need, hence the question. Seems a waste to do it in Java with all those usual bumper tests we need to do with a Cursor.
SELECT *
FROM myTable LIMIT 2
OFFSET
(SELECT _id
FROM myTable
WHERE time<=1332200003002
ORDER BY time DESC LIMIT 1) - 1;
What this does:
Select 2 entries from the table. The offset of the first entry is selected as follows:
Choose the latest row where time <= 1332200003002, and calculate its offset from the first row.
The -1 at the end is needed if your _id values start at 1 rather than 0. (Change this value as needed to convert your _id values into zero-based offsets.)
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;