SQLite full-text search with multiple tokens using a prepared statement - sqlite

Given the following tables:
create table index(name text, docid int);
create virtual table docs using fts4();
The following query works as intended when querying for a single token (for example: march, or bad):
select name from index where docid in (select docid from docs where docs match ?)
But how can I query for more than one token (say, bad bed)? Binding the string bad bed directly does not work (always selects nothing), neither surrounding the placeholder or the string with double quotes, nor using AND to MATCH each token separetly (this last one throws an error).

Using intersect does work, but it's clunky and innefficient when searching for many tokens:
select name from index where docid in (
select docid from docs where docs match ?
intersect
select docid from docs where docs match ?
intersect
...
)
Each ? is paired with a single token.

You can use the concatenation operator || in sqlite. '' would be the empty string
SELECT * FROM table WHERE docs MATCH '' || ? || ' ' || ? || ' ' || ? || ''
Make sure there is a space between every token or an ' AND '.
Update
Actually it doesn't work. It seems there are tokenator issues with this approach. Its better to concatenate all the tokens with the space and bind the resulting string with a single '?'

There are operators within the match syntax in FTS so you can use AND, OR and NOT.
See here for documentation
e.g.
-- Return the docid values associated with all documents that contain the
-- two terms "sqlite" and "database", and/or contain the term "library".
SELECT docid FROM docs WHERE docs MATCH 'sqlite AND database OR library';

Related

How to concatenate a Select query inside a INSTR() in SQLite?

I was trying to order a result set by the order of the values in an IN() clause.
SELECT * FROM CrossReference WHERE cross_reference_id IN (SELECT Id FROM FilteredIds)
So I tried to find a function such as MySql FIELD(). Then I found these answers (answer1, answer2) which explain how to do the exact thing on SQLite using the INSTR().
SELECT *, INSTR(',GDBR10,GDBR5,GDBR30,', ',' || ticker || ',') POS
FROM tbl
WHERE POS>0
ORDER BY POS;
So it's working as expected, but I want to populate the ids dynamically using a select query. I tried many approaches, but nothing seemed to work. Here is the last one I tried. It gave me just one result row (a result related to the first filterId).
SELECT *, INSTR (','||(SELECT id FROM FilteredIds)||',', ',' || cross_reference_id || ',') POS FROM CrossReference WHERE POS>0 ORDER BY POS;
So I guess I'm making some kind of mistake when concatenating the SELECT query with the rest of the code. Because when I manually enter the filtered Ids it works and returns results according to the entered filter ids.

REGEXP_SUBSTR return all matches (mariaDB)

I need to get all the matches of a regular expression in a text field in a MariaDB table. As far as I know REGEXP_SUBSTR is the way to go to get the value of the match of a regular expression in a text field, but it always returns after the first match and I would like to get all matches.
Is there any way to do this in MariaDB?
An example of the content of the text field would be:
#Generation {
// 1
True =>
`CP?:24658` <= `CPV?:24658=57186`;
//`CP?23432:24658` <= `CPV?:24658=57186`
// 2
`CP?:24658` <> `CPV?:24658=57178` =>
`CP?:24656` <> `CPV?:24656=57169`;
And the select expression that I'm using right now is:
select REGEXP_SUBSTR(textfield,'CP\\?(?:\\d*:)*24658') as my_match
from table
where id = 1243;
Which at the moment returns just the first match:
CP?:24658
And I would like it to return all matches:
CP?:24658
CP?23432:24658
CP?:24658
Use just REGEXP to find the interesting rows. Put those into a temp table
Repeatedly process the temp table -- but remove the SUBSTR as you use it.
What will you be doing with each substr? Maybe that will help us devise a better approach.

Creating a new table in sqlite database [duplicate]

I'm having some strange feeling abour sqlite3 parameters that I would like to expose to you.
This is my query and the fail message :
#query
'SELECT id FROM ? WHERE key = ? AND (userid = '0' OR userid = ?) ORDER BY userid DESC LIMIT 1;'
#error message, fails when calling sqlite3_prepare()
error: 'near "?": syntax error'
In my code it looks like:
// Query is a helper class, at creation it does an sqlite3_preprare()
Query q("SELECT id FROM ? WHERE key = ? AND (userid = 0 OR userid = ?) ORDER BY userid DESC LIMIT 1;");
// bind arguments
q.bindString(1, _db_name.c_str() ); // class member, the table name
q.bindString(2, key.c_str()); // function argument (std::string)
q.bindInt (3, currentID); // function argument (int)
q.execute();
I have the feeling that I can't use sqlite parameters for the table name, but I can't find the confirmation in the Sqlite3 C API.
Do you know what's wrong with my query?
Do I have to pre-process my SQL statement to include the table name before preparing the query?
Ooookay, should have looked more thoroughly on SO.
Answers:
- SQLite Parameters - Not allowing tablename as parameter
- Variable table name in sqlite
They are meant for Python, but I guess the same applies for C++.
tl;dr:
You can't pass the table name as a parameter.
If anyone have a link in the SQLite documentation where I have the confirmation of this, I'll gladly accept the answer.
I know this is super old already but since your query is just a string you can always append the table name like this in C++:
std::string queryString = "SELECT id FROM " + std::string(_db_name);
or in objective-C:
[#"SELECT id FROM " stringByAppendingString:_db_name];

SQLite SELECT statement where column equals zero

I'm preety new to SQLite.
I have a preety basic question.. Why can't I select rows where specific column equals zero?
The is_unwanted column is type TINYINT (which I see in SQLite basically means INTEGER)
So, I have only one record in the database (for testing).
When I try
SELECT is_unwanted FROM 'urls'
I get a result of "0" (zero), which is fine because that column contains the actual number 0.
I tried =>
SELECT * FROM 'urls' WHERE is_unwanted = 0
And got NO result, but
SELECT * FROM 'urls' WHERE is_unwanted <> 0
gives me result.
What am I doing wrong??
Try running
select '{' || is_unwanted || '}' from urls
to see if the value in the database is really a string containing spaces.
SQLite is a dynamically typed database; when you specify TINYINT is is a hint (SQLite uses the term "affinity") for the column. You can use
select is_unwanted, typeof(is_unwanted) from urls
to see the values with their types.
You could try:
SELECT * FROM urls WHERE coalesce(is_unwanted,'') = ''

Using prepared statements and full-text-search in SQLite

I'm using the SQLite C interface to write an application. Since I like security, I'm using prepared statements to query the database. In one such query, I'm selecting rows from a virtual database using the MATCH keyword for full-text-searching. Here's an example:
SELECT * FROM Emails
WHERE ( Subject LIKE ?001 OR ?001 IS NULL )
AND ( Author LIKE ?002 OR ?002 IS NULL )
AND ( Body MATCH ?003 OR ?003 IS NULL )
This allows the user to enter any terms (Subject, Author, or Body) individually or in any combination to do a search. Any term that isn't entered, I'll bind NULL to that parameter. The problem with that statement is that you can't use the OR keyword with the MATCH keyword. I'm looking for a statement I can use with the MATCH keyword to return all rows if not searching in the Body column. Is there such a statement?
I suggest the following:
SELECT * FROM emails
WHERE ...
AND ( CASE (SELECT COUNT(*) FROM emails WHERE body MATCH ?003)
WHEN 0 THEN 1
ELSE body MATCH ?003
END )
I ended up modifying the SQL statement at runtime to replace MATCH with LIKE '%'. Not very elegant, but it works for now.

Resources