SQLite Left Outer Join not working as expected in Android - android

I have prepared a left outer join query that should be returning non-null data when a record exists in my BLOB table that matches keys with a record in my INCIDENT table.
Essentially, the data and query looks like this:
Table A
Key
Table B
Key
Blob
and the query I'm running, should show all records from A, and the 'hasb' column should be 1 if there's a record in B with a matching key column, or 0 if there isn't. So the query should look like this:
SELECT A.*, ifnull(A.Key = B.Key, 0) as hasb FROM A
LEFT OUTER JOIN B ON A.Key = B.Key
Ok, so my problem is, this query seems to work everywhere I try to use it EXCEPT on the Android device.
Here's a SQLFiddle with the actual tables and query in question. Note that the query works there.
http://sqlfiddle.com/#!7/89e7d/4
Anyone know why this doesn't work on Android? The device I'm testing with is a Samsung Galaxy S 3 running Android 4.1.1.

I think you're overcomplicating things with the ifnull.
This is the query in your fiddle:
SELECT ifnull( a.userid = b.userid, 0 ) FROM incidentdata AS a
LEFT OUTER JOIN incidentblob AS b
ON ( a.userid = b.userid ) AND ( a.incidenttag = b.incidenttag );
This is the query I would write:
SELECT a.userid, a.incidenttag, b._id, b._id is not null hasb
FROM incidentdata AS a
LEFT JOIN incidentblob AS b
ON a.userid = b.userid AND a.incidenttag = b.incidenttag
Does it work? If it doesn't please, provide the SQLite version you're using. Anyway, I'm pretty sure the issue resides in the following items:
Understanding that null = null does not return TRUE but rather null
null values being present in both b.userid as well as a.incidenttag after performing the left join

I know your query "seems to work everywhere".
However, I suggest you an alternative one:
SELECT a.*, b.ROWID IS NOT NULL AS hasb FROM a LEFT JOIN b ON a.key IS b.key;
Differences:
Avoid IFNULL function call;
a.key IS b.key will handle a null key value;
checking b.ROWID is b row data independent.

Related

Get Wordpress posts with images link

I want to get wordpress posts with specific category and link of images.
As you know images links save to database in guid column, when post_type = attachment.
and ID of post and post_parent are the same.
Now I want to get posts and join guid column to same ID.
When I added Inner join to combine attachment and post, I got error!
Please help me, if you know the way that I can get post with specific category and images link of each post.
Here is my code:
SELECT
*
FROM
wp_posts p,
wp_postmeta m,
wp_terms t,
wp_term_taxonomy tt,
wp_term_relationships tr,
wp_terms t2,
wp_term_taxonomy tt2,
wp_term_relationships tr2
LEFT JOIN wp_posts AS p2
ON
p.ID = p2.post_parent
WHERE
p.post_type = 'post' AND p.post_status = 'publish'
AND p.ID = tr.object_id
AND t.term_id = tt.term_id
AND tr.term_taxonomy_id = tt.term_taxonomy_id
AND tt.taxonomy = 'category'
AND tt.term_id = t.term_id
AND t.name = 'Fashion'
GROUP BY
p.ID
ORDER BY
id
DESC
MySQL said:
#1054 - Unknown column 'p.ID' in 'on clause'
I suspect that the problem is due to mixing the old school comma syntax with the newer JOIN keyword.
Relevant excerpt from MySQL Reference Manual:
INNER JOIN and , (comma) are semantically equivalent in the absence of a join condition: both produce a Cartesian product between the specified tables (that is, each and every row in the first table is joined to each and every row in the second table).
However, the precedence of the comma operator is less than that of INNER JOIN, CROSS JOIN, LEFT JOIN, and so on. If you mix comma joins with the other join types when there is a join condition, an error of the form Unknown column 'col_name' in 'on clause' may occur. Information about dealing with this problem is given later in this section.
The easiest way to avoid this problem is to ditch the old school syntax for the join operation, use the JOIN keyword instead.
(It's great that the comma syntax is still valid, to provide backwards compatibility with existing SQL. But there's no good reason new development should use the comma syntax.)
Aside from that, there's a couple of big rock issues that stick out to me.
Seems like there's a lot of join conditions missing
Using * for the SELECT list in development can be useful shortcut, but we usually list the expressions we need to return, especially if we want to return id column from multiple tables, where we like to assign a column alias to avid duplicate columns names.
Relying on the non-standard extension to GROUP BY (when only_full_group_by is omitted from sql_mode to return values from "some" row in the collapsed group
Those all look like serious problems to me.
We can re-write the OP query to use JOIN keyword in place of comma syntax, and relocating conditions to the ON clause, this highlights what looks like missing join conditions:
SELECT *
FROM wp_posts p
JOIN wp_postmeta m
-- ON ???
JOIN wp_terms t
ON t.name = 'Fashion'
JOIN wp_term_taxonomy tt
ON tt.term_id = t.term_id
AND tt.taxonomy = 'category'
JOIN wp_term_relationships tr
ON tr.object_id = p.id
AND tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms t2
-- ON ???
JOIN wp_term_taxonomy tt2
-- ON ???
JOIN wp_term_relationships tr2
-- ON ??
LEFT
JOIN wp_posts AS p2
ON p2.post_parent = p.id
WHERE p.post_type = 'post'
AND p.post_status = 'publish'
GROUP
BY p.id
ORDER
BY p.id DESC
Where we are going to omit any join condition, and just match all rows to all other rows, then my preference is to include the (optional) CROSS keyword, as an aid the future reader, to signal that the omission of a join condition is by design, and not an oversight.

How do I combine two queries into one?

This is for Android SQLite. I have two queries like this:
select * from table where name='name';
and
select * from table where name!='name' order by name;
I want to create a statement which combines these two queries. I tried union all but I can't do order by one statement and then combine. I tried this:
select * from table where name='name'
union all
select * from table where name!='name' order by name;
All it did is to combine the queries and then order by name. I don't want that. I want to do order by on the second statement first and then combine them.
To put the question differently, here is my data:
Name
a
b
c
d
e
f
g
h
i
j
But I want the output to be:
Name
g
a
b
c
d
e
f
h
i
j
I want to get one row to the top and then order the rest of the rows. Any help is appreciated.
No need to use temporary tables, you need to add an additional column to sort on. Something like this:
select 1, * from table where name='name'
union all
select 2, * from table where name!='name'
order by 1, name;
I don't have a sqlite install right now, but this trick should work. (you may have to add an alias to the first column).
Unless there is some other attribute of the table you can use to provide sorting that allows a join between the two selects as in How to combine two sql queries? then I think you'll have to store the result of the query that should float to the top in a temporary table and then add the sorted results to that table before storing it.
I've never used temporary tables in Android so can't provide an example but as far as I'm aware it's possible.
I'd recommend running the two queries separately and then combining the results in code if that's possible in your situation.
According to the SQLLite docs this cannot be done with a UNION or UNION ALL because those operations must be performed on a simple select, (ones without Order by).
http://www.sqlite.org/lang_select.html
There's probably a very clever way to do this that I don't know, which generally leads me to just do two queries and combine the results in java.
[EDIT] And Jhovanny has the very clever way to do it.
Can't test it right now, but something like this should work:
select t.*, case when name = 'name' then 0 else 1 as o from table t order by o, name;
Then you don't have the two selects nor the union. Assuming you can use a case statement in sqlite on android.

SQLite returns incorrect number of rows as a cursor

I've come across a strange problem while writing an android app.
I've got a particularly messy piece of SQL (which I'm in the process of tidying) which returns a cursor for a custom listadapter.
The problem is part way through tweaking my code using SQLITEMAN I've found I'm getting correct results from SQLITEMAN (as I do some aggregation) but when I run it through android I get no results on a database that I copied directly from android using DDMS.
Again, yes I know the code is messy, but it does get results. If the code is actually the problem, it can be tidied up.
SELECT -1 as _id, 'player1|player2' as players, null as turntext, null as turnimage,30 as maxturns,19 as curturns
union
SELECT games._id,games.players,lastturn.turntext,lastturn.turnimage,games.maxturns,lastturn.curturns
FROM games
inner join (select _id,turntext,turnimage,t.gameid, tb.curturns from turns t inner join (select tb.gameid,count(tb._id) as curturns from turns tb group by tb.gameid) tb on tb.gameid = t.gameid where _id = (select max(_id)
from turns where gameid = t.gameid ) group by t.gameid) lastturn on lastturn.gameid = games._id order by games._id desc
EDIT: I've re-written the SQL so there are only 2 inner loops. Not surprisingly the issue still persisted.
After more investigation I've now got a new lead: when I was inserting the entries with the code below, I think the cast of the return value to int, while acceptable based on my methods, might have been stuffing up the submission of the values...
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_GAMEID,gameId);
initialValues.put(KEY_TURNTEXT,text);
initialValues.put(KEY_TURNIMAGE,imageBytes);
return (int)mDb.insert(TABLE_NAME, null, initialValues);

Android: SQLite FTS3 slows down when fetching next/previous rows

I have a sqlite db that at the moment has few tables where the biggest one has over 10,000 rows. This table has four columns: id, term, definition, category. I have used a FTS3 module to speed up searching which helped a lot. However, now when I try to fetch 'next' or 'previous' row from table it takes longer than it was before I started using FTS3.
This is how I create virtual table:
CREATE VIRTUAL TABLE profanity USING fts3(_id integer primary key,name text,definition text,category text);
This is how I fetch next/previous rows:
SELECT * FROM dictionary WHERE _id < "+id + " ORDER BY _id DESC LIMIT 1
SELECT * FROM dictionary WHERE _id > "+id + " ORDER BY _id LIMIT 1
When I run these statements on the virtual table:
NEXT term is fetch within ~300ms,
PREVIOUS term is fetch within ~200ms
When I do it with normal table (the one created without FTS3):
NEXT term is fetch within ~3ms,
PREVIOUS term is fetch within ~2ms
Why there is such a big difference? Is there any way I can improve this speed?
EDITED:
I still can't get it to work!
Virtual table you've created is designed to provide full text queries. It's not aimed to fast processing standard queries using PK in where condition.
In this case there is no index on your _id column, so SQLite probably performs full table scan.
Next problem is your query - it's totally inefficient. Try something like this (untested):
SELECT * FROM dictionary WHERE _id = (select max(_id) from dictionary where _id < ?)
Next thing you can consider is redesign of your app. Instead of loading 1 row you, maybe you should get let's say 40, load them into memory and make background data loading when there is less than n to one of the ends. Long SQL operation will become invisible to user even if it'll last 3s instead of 0,3s
If you're running LIMIT 1 to begin with, you can remove the order by clause completely. This may help. I'm not familiar with FTS3, however.
You could also just flat out assign your id variable a ++ or -- and assert `WHERE _id = "+id+" LIMIT 1" which would make a single lookup instead of < or >.
Edit: and now that I look back at what I typed, if you do it that way, you can just remove LIMIT 1 completely, since your _id is your pk and must be unique.
hey look, a raw where clause!

SQL select specific fields in JOIN query

i'm writing an Android app and i've run into a bit of a roadblock involving databases. the way Android handles databases, i cannot refer to names in the result set by the usual 'tablename.colname' method, so this presents a huge issue when any tables in the database contain the same column name. what further complicates the issue, is that any table that is used by a ViewAdapter to display the data to the user (as in my application), must contain a field named "_id" as an autoincrement primary key int. therefore, some tables MUST have identical column names. however, to avoid this, it is possible to use an "AS" clause in a statement to rename the value in question. however, i'm using a rather long statement and i don't know how to limit the columns returned on a JOINed table. what i have is this, and it's completely illegal in android due to the 'tablename.colname' references. i actually added the table names in to make the statement more readable, but i can't use them:
SELECT call._id AS android_call_id,
call.phone,
call.time,
call.duration
call.duration_billed
call.pending
call.call_id
call.job_id
FROM call
LEFT OUTER JOIN phone ON call.phone_number=phone.phone
LEFT OUTER JOIN job ON job._id=call.job_id
WHERE call.pending=1 ORDER BY job._id
but what i need, is to rename the job._id to something else using an "AS" statement, same as with the 'call._id' field in the first part of the query. how do i achieve this renaming in a JOIN?
edit:
progress so far. i think i've worked out the syntax errors, but i get another runtime error "no such column 'job._id', which may be related to #Tom H. comment
edit 2:
turns out Tom was right, and i adjusted accordingly, but it doesn't work:
SELECT call._id AS android_call_id,
call.phone,
call.time,
call.duration,
call.duration_billed,
call.pending,
call.call_id,
call.job_id,
job._id AS android_job_id,
job.job_name,
job.job_number
FROM call
LEFT OUTER JOIN phone ON call.phone_number=phone.phone
LEFT OUTER JOIN job ON job._id=call.job_id
WHERE call.pending=1 ORDER BY job._id
error:
05-24 16:50:37.561: ERROR/Minutemaid - Service(7705): oops: ambiguous column name: call._id: , while compiling: SELECT call._id AS android_call_id,call.phone_number,call.time,call.duration,call.duration_billed,call.pending,call.call_id,call.job_id,job._id AS android_job_id,job.job_name,job.job_number FROM call LEFT OUTER JOIN phone ON call.phone_number=phone.phone LEFT OUTER JOIN call ON call.job_id=job._id WHERE call.pending=1 ORDER BY job._id
Can't you simply use AS to alias all of the tablename.columnname references to unique names in the result set?
You can simply create a VIEW that restricts columns selectable in a table and assigns another name to them.
You can try massaging the table names before you join them by using sub-queries with AS in the FROM clause. For example:
select c_phone, c_id, p_id
from (select id as c_id, phone as c_phone, phone_number as c_phone_number, ... from call) as c
left outer join (select id as p_id, phone as p_phone, ... ) as p
on c_phone_number = p_phone
...
If the limitation is just that you can't use table names to distinguish between columns but can use correlation names then simpler is:
select c.id, c.phone, p.id as "p_id" from ... call c join phone p

Categories

Resources