SQLite User Forum

How to select multiple ’LIKE’ results
Login

How to select multiple 'LIKE' results

(1.1) By Trudge on 2022-09-23 17:42:42 edited from 1.0 [link] [source]

I have a database of articles, some of which have several keyword links. I would to create a select statement that gets an article with keyword1 AND keyword1.

CREATE TABLE sqlite_sequence(name,seq);
CREATE TABLE IF NOT EXISTS "keywords" (
	`id`	integer NOT NULL,
	`keyword`	text,
	`authorid`	integer,
	`titleid`	integer,
	PRIMARY KEY(`id` AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "authors" (
	`id`	integer NOT NULL,
	`author`	text,
	`authorid`	integer,
	`titleid`	integer,
	PRIMARY KEY(`id` AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "docs" (
	`id`	INTEGER,
	`authorid`	INTEGER,
	`title`	TEXT,
	`source`	TEXT,
	`pubyear`	INTEGER,
	`notes`	TEXT,
	`filename`	TEXT,
	`issue`	TEXT,
	`titleid`	INTEGER,
	PRIMARY KEY(`id` AUTOINCREMENT)
);
I have a query with an OR but can't seem to get it working with an AND. Of course this doesn't work because a keyword can't be one thing AND another. The logic of how this can work stumps me.
select title,keywords.keyword from docs
join keywords on docs.authorid=keywords.authorid
and docs.titleid=keywords.titleid
where keyword in("Geometry")
or keyword in ("Mathematics")
order by title;
Is it possible to get a query working with keyword1 AND keyword2?

(2) By Keith Medcalf (kmedcalf) on 2022-09-23 18:10:49 in reply to 1.1 [link] [source]

Obviously not. keyword is single attribute of a single row of a single table and cannot have more than one value at a time.

However, you may find all the "stuffs" with keyword1 and then all the same "stuffs" that have keyword2, and then INTERSECT them to find the "common stuffs".

select title 
  from docs
  join keywords 
    on docs.authorid == keywords.authorid
   and docs.titleid == keywords.titleid
 where keyword == 'Geometry'
INTERSECT
select title 
  from docs
  join keywords 
    on docs.authorid == keywords.authorid
   and docs.titleid == keywords.titleid
 where keyword == 'Geometry'
order by title;

NOTE: Fixed Quotes: " is for identifiers, ' is for text strings
NOTE: Fixed keyword in (entry) to keyword == entry

It would probably be perspicacious to normalize your database.

(3) By Jon (gocoder) on 2022-09-23 18:14:33 in reply to 1.1 [source]

I was also able to accomplish this with a CTE. Certainly not saying this is a better solution.

WITH q AS (
	SELECT k1.titleid, k2.authorid
	FROM keywords k1
		INNER JOIN keywords k2
		ON k1.titleid=k2.titleid
		AND k1.authorid=k2.authorid
	WHERE k1.keyword = 'Geometry'
		AND k2.keyword='Mathematics'
)
SELECT title
FROM docs JOIN q
	ON docs.authorid=q.authorid
	AND docs.titleid=q.titleid
ORDER by title;

(4) By David Raymond (dvdraymond) on 2022-09-23 18:42:02 in reply to 1.1 [link] [source]

Two possibilities

Sub-select:

select title
from docs
where
(
    select count(*)
    from keywords
    where authorid = docs.authorid
    and titleid = docs.titleid
    and keyword in ('Geometry', 'Mathematics') --Your list of keywords here
) = 2 --How many keywords are in your list
;


CTE

with wanted_keywords (keyword) as (values ('Geometry'), ('Mathematics'))
select title
from docs
inner join keywords
    on keywords.authorid = docs.authorid
    and keywords.titleid = docs.titleid
    and keyword in wanted_keywords
group by docs.id
having count(*) = (select count(*) from wanted_keywords)
;

(5) By Keith Medcalf (kmedcalf) on 2022-09-23 19:23:56 in reply to 1.1 [link] [source]

If the tables were normalized (such that the keyword 'Geometry' is the same keyword when attached to title 'Jiggling Ducks' and 'Dumpy frumps'. As in perhaps:

create table keyword
(
    id integer primary key,
    keyword text collate nocase unique
) strict;
create table docs 
(
    id integer primary key,
    authorid INTEGER,
    title TEXT,
    source TEXT,
    pubyear INTEGER,
    notes TEXT,
    filename TEXT,
    issue TEXT,
    titleid INTEGER
);
create table linkage
(
    keyid integer not null references keyword,
    docid integer not null references docs,
    primary key (keyid, docid),
    unique (docid, keyid)
) without rowid;

THen one could use something like:

select *
  from docs
 where id in ( select docid from linkage, keywords where keyid==id and keyword = 'keyword1'
               intersect
               select docid from linkage, keywords where keyid==id and keyword = 'keyword2'
               intersect
               ...
             )
;

(6) By Trudge on 2022-09-23 20:30:28 in reply to 1.1 [link] [source]

I believe I have solved my problem using views.

create temp view geo as select title,keyword from docs,keywords
where keyword like "%Geometry%"
and docs.authorid=keywords.authorid
and docs.titleid=keywords.titleid;
create temp view math as select title,keyword from docs,keywords
where keyword like "%Mathematics%"
and docs.authorid=keywords.authorid
and docs.titleid=keywords.titleid;
Then my query gave me 2 results:
select geo.title,geo.titleid,geo.keyword,math.title,math.titleid,math.keyword from geo,math 
where geo.title=math.title;
title|keyword|title|keyword
Curves of Constant Width|Geometry|Curves of Constant Width|Mathematics
1 Peg in 3 Holes|Geometry|1 Peg in 3 Holes|Mathematics
After checking the DB I'm convinced these are the only titles that have both 'Geometry' AND 'Mathematics' as keywords. What a great day!

(7) By Trudge on 2022-09-23 20:45:59 in reply to 6 [link] [source]

Wow, some excellent suggestions here. Thank you all for the inputs. My use of sqlite has been rather simplistic so far, and this was a good exercise for me.