Question

Let's say I have three tables. One table (TblArticles) describes, for example, articles. It has an auto-incrementing key, article name and other data about it, such as:

pkIndex | sName | Other Data...
--------|-------|--------------
   1    | Name1 | Data1
   2    | Name2 | Data2
   3    | Name3 | Data3

Et cetera. Then I have a table of tags (TblTags), for instance:

pkIndex | sName
--------|--------
   1    | Politics
   2    | Science
   3    | Fun

Et cetera. And I have a relations table (TblArticlesTagsRel) for them, since one article can have multiple tags, and each tag can be applied to multiple articles:

indArticle | indTag
-----------|-------
     1     |   1
     1     |   2
     2     |   1
     2     |   3
     3     |   3

Et cetera.

So, my question is, what is the best way to select rows from TblArticles which fit certain conditions (like sName being something specific) AND having several specified tags (for example, Politics AND Science) applied to them? Note that for this I need all specified tags to be present for every selected row, not either one of the specified tags.

I know I can do it by selecting which results of a previous select fit the conditions for another tag, but if I choose several tags, the select query becomes increasingly more and more complex because of this nesting and I do believe there has to be another, easier way.

I've tried using AND in various ways, but I can not figure out exactly how to use it in this case.

Oh, and, if it's important, I'm using SQLite.

EDIT: So, while the answer from greyalien007 didn't work for me, it did point me in the right direction, and that's what I have used in the end:

SELECT a.* FROM TblArticles a, (SELECT indArticle FROM TblArticlesTagsRel WHERE indTag IN(1,2) GROUP BY indArticle HAVING COUNT(indArticle)=2) WHERE (a.pkIndex = indArticle) AND (sName = "title");
Was it helpful?

Solution 3

So, while the answer from greyalien007 didn't work for me, it did point me in the right direction, and that's what I have used in the end:

select * from TblArticles,
(
   select indArticle from TblArticlesTagsRel
   where indTag IN(1, 2) 
   group by indArticle  
   having count(indArticle) = 2
)
where (pkIndex = indArticle) 
and (sName = "title");

OTHER TIPS

I'm not sure what SQLite supports, but if it does derived tables like tsql..

Here's me setting up your data:

create table TblArticles (pkIndex int, sName varchar(100))
create table TblTags(pkIndex int, sName varchar(100))
create table TblArticlesTagsRel (indArticle int, indTag int)

insert into TblArticles(pkIndex, sName)
values
   (1, 'Name1')
,  (2, 'Name2')
,  (3, 'Name3')

insert into TblTags(pkIndex, sName)
values
   (1, 'Science')
,  (2, 'Politics')
,  (3, 'Fun')

insert into TblArticlesTagsRel(indArticle, indTag)
values
  (1, 1)
, (1, 2)
, (2, 1)
, (2, 3)
, (3, 3)

Here's me attempting to solve the issue:

select a.*
from TblArticles a
inner join (
    select indArticle, count(indArticle) as 'tagCount'
    from TblArticlesTagsRel r
        inner join TblTags t on r.indTag = t.pkIndex
where t.sName in ('Politics', 'Science')
group by indArticle
) d on a.pkIndex = d.indArticle
where sName = 'Name1' and tagCount = 2

Not optimized, but should do the job:

SELECT * FROM TblArticles WHERE sName="title" AND
EXISTS (
    SELECT * FROM TblArticlesTagsRel
    JOIN TblTags ON TblTags.pkIndex=TblArticlesTagsRel.indTag
    WHERE TblArticlesTagsRel.indArticle=TblArticles.pkIndex AND
    TblTags.sName='Politics'
) AND EXISTS (
    SELECT * FROM TblArticlesTagsRel
    JOIN TblTags ON TblTags.pkIndex=TblArticlesTagsRel.indTag
    WHERE TblArticlesTagsRel.indArticle=TblArticles.pkIndex AND
    TblTags.sName='Science'
);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top