I want to perform a query in which the WHERE clausule has the following condition:
one MATCH condition over a column in a FTS3 table
OR
another not MATCH condition over a column in a non FTS table.
Example:
Say that I have two tables
books_fts, which is a table with a content column for full text search.
books_tags, which is non FTS table with tags.
I want to search all the books that either contain 'Dikjstra' in their content or are tagged with the 'algorithm' word. So I run this query:
SELECT * from books_fts
LEFT OUTER JOIN books_tags ON books_fts.fk_id = books_tags.id
WHERE (books_fts MATCH 'content:%Dijkstra*')
OR (books_tags.tag = 'algorithm')
I think the query is right, and if I run it with either one of the OR clausules, it works.
However, when running it with the two clausules I get the following error:
unable to use function MATCH in the requested context
Seems to me that I cannot combine a MATCH with a non MATCH in the WHERE clause, even if each of them apply to different tables (one FTS and another non FTS).
Is this true? I cannot find information on it.
NOTE: if the causules are separated with AND instead of OR the query is valid.
Thanks.
It seems it's a known issue in SQL:
http://sqlite.1065341.n5.nabble.com/FTS3-bug-with-MATCH-plus-OR-td50714.html
Related
I am making a dictionary of over 20,000 words in it. So, to make it work faster when search data, i am using fts3 table to do it.
my select query:
Cursor c=db.rawQuery("Select * from data where Word MATCH '"+word+"*'", null);
Using this query, it will show all the word that contain 'word' , but what i want is to get only the word that contain the beginning of the searching word.
Mean that i want it work like this query:
Cursor c=db.rawQuery("Select * from data where Word like '"+word+"%'", null);
Ex: I have : apple, app, and, book, bad, cat, car.
when I type 'a': i want it to show only: apple, app, and
What can i solve with this?
table(_id primary key not null autoincrement, word text)
FTS table does not use the above attributes. It ignores data type. It does not auto increment columns other than the hidden rowid column. "_id" will not act as a primary key here. Please verify that you are implementing an FTS table
https://www.sqlite.org/fts3.html
a datatype name may be optionally specified for each column. This is
pure syntactic sugar, the supplied typenames are not used by FTS or
the SQLite core for any purpose. The same applies to any constraints
specified along with an FTS column name - they are parsed but not used
or recorded by the system in any way.
As for your original question, match "abc*" already searches from the beginning of the word. For instance match "man*" will not match "woman".
FTS supports searching for the beginning of a string with ^:
SELECT * FROM FtsTable WHERE Word MATCH '^word*'
However, the full-text search index is designed to find words inside larger texts.
If your Word column contains only a single word, your query is more efficient if you use LIKE 'a%' and rely on a normal index.
To allow an index to be used with LIKE, the table column must have TEXT affinity, and the index must be declared as COLLATE NOCASE (because LIKE is not case sensitive):
CREATE TABLE data (
...
Word TEXT,
...
);
CREATE INDEX data_Word_index ON data(Word COLLATE NOCASE);
If you were to use GLOB instead, the index would have to be case sensitive (the default).
You can use EXPLAIN QUERY PLAN to check whether the query uses the index:
sqlite> EXPLAIN QUERY PLAN SELECT * FROM data WHERE Word LIKE 'a%';
0|0|0|SEARCH TABLE data USING INDEX data_Word_index (Word>? AND Word<?)
I need to know if it's possible to get all rows from fts table when user doesn't type any text.
I need this, because when app starts, I need to show all rows.
How can I achieve this?
There is no MATCH expression that would return all records.
Just make a special case for the empty string, and use the plain
SELECT * FROM MyTable
I've got a fairly complicated query (multiple joins) on a normalized sqlite database. The query does a SELECT * to enable some automated attribute selection logic (so I can't eliminate the "*")
The problem I am having is that my result set contains multiple columns with the same attribute name. For example, one attribute common to each table in the query is "_id". When I go to call "cursor.getColumnIndex("_id")" the value returned is always the index of the last "_id" attribute in the result set column list (i.e. not the one I want). I'd love to be able to use my SQL alias prefixes like cursor.getColumnIndex("A._id") but that is not working.
QUESTIONs
It appears that cursor.getColumnIndex(AttributeName) returns the index of the last "AttributeName". Can anyone confirm this?
Also, any suggestions on how return the index of the 1st attribute with "AttributeName"? or better the Xth attribute having "AttributeName"?
You can do this:
SELECT _id as myID, * FROM myTable
This means the _id field will appear twice for each table in your results, but one of the two columns will have a unique name which should enable you to find it.
Unfortunately the documentation doesn't mention anything about what you need to do, so I am assuming it cannot be done.
However, you say
The query does a SELECT * to enable some automated attribute selection
logic (so I can't eliminate the "*")
What is this 'automated attribute selection logic' you speak of? Why do you require this?
An oder solution is:
"SELECT tableName.columnName FROM tableName"
and then do the same with:
cursor.getColumnIndex("tableName.columnName");
This is what MS-Access does. You can create a query and then see the generated SQL code (simply going to 'view' menu and selecting 'SQL view' from your query dessign window)
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
Let's say an SQLite database column contains the following value:
U Walther-Schreiber-Platz
Using a query I'd like to find this record, or any similar records, if the user searches for the following strings:
walther schreiber
u walther
walther-schreiber platz
[Any other similar strings]
However I cannot figure out any query which would do that. Especially if the user does not enter the - character.
Example:
select * from myTable where value like '%walther schreiber%'; would not return the result because of the missing -.
Thanks,
Robert
So, as I said in my comment, I think you can have a query along the lines of:
select * from myTable where LOWER(value) like <SearchValue>
I'm assuming you're collecting the <SearchValue> from the user programmatically, so would be able to do the following: <SearchValue> would need to be: The user's search string, appropriately cleansed to avoid SQL injection attacks, converted to lower case, with all of the spaces converted to '%', so that they match zero or more characters...
So you would have (for example):
select * from myTable where LOWER(value) like '%walther%schreiber%'
select * from myTable where LOWER(value) like '%walther-schreiber%platz%'
etc... however, this does assume that the word order is the same, which in your examples it is...