SQL内部连接存在多个到多个实例

时间:2017-11-22 09:49:00

标签: mysql sql many-to-many

我有两张桌子

Campaigns ( name, date, url, etc. )
campaign_tags ( campaign_name, tag ) 

我正在尝试选择所有包含标签列表的广告系列,即

SELECT * 
from campaigns
inner join campaign_tags on campaigns.name=campaign_tags.name
where ( list of campaign tags ) in ( list of tags ) 

我有标签列表(它们是由“,”分隔的字符串),我希望列表中包含所有标签的广告系列。我怎么能这样做?

编辑;样本数据:

Campaign: "MyCampaign", "22/11/2017","https://stackoverflow.com/posts/47431394" "MyCampaign2", "22/11/2017","https://stackoverflow.com/posts/47431394" CampaignTags: "MyCampaign","sports" "MyCampaign","stackoverflow" "MyCampaign","Life" "MyCampaign2","food"

如果我要查询的列表是"sports","life",stackoverflow","health","programming",那么“MyCampaign”,“22/11/2017”等应该出现,因为它的所有标签都包含在列表中,但是“MyCampaign2”不应该“T;如果我查询"food","sports",那么“MyCampaign2”应该出现,如果我要查询“体育”,那么什么都不应该出现。

3 个答案:

答案 0 :(得分:1)

这可以让那些拥有这些标签的广告系列不是单一标签而是所有这些标签

SELECT c.*
FROM campaigns c
INNER JOIN campaign_tags ct ON c.name=ct.name
WHERE ct.tag IN ( "sports","life" )
GROUP BY c.name
HAVING COUNT(DISTINCT ct.tag) = SUM(ct.tag IN ( "sports","life" )) 

Demo

答案 1 :(得分:1)

作为变种

SELECT *
FROM Campaigns
WHERE name IN(
          SELECT campaign_name
          FROM campaign_tags
          WHERE tag IN("sports","life")
          GROUP BY campaign_name
          HAVING COUNT(tag)=LENGTH('"sports","life"')-LENGTH(REPLACE('"sports","life"',',',''))+1 -- count of tags
        )

SQL小提琴 - http://sqlfiddle.com/#!9/8870c/10

新条件的第二个变体

SELECT *
FROM Campaigns
WHERE name IN(
          SELECT campaign_name
          FROM campaign_tags
          GROUP BY campaign_name
          HAVING
            COUNT(CASE
                    WHEN tag IN("sports","life","stackoverflow","health","programming")
                    THEN tag
                  END)=COUNT(tag)
        )

SQL小提琴 - http://sqlfiddle.com/#!9/ed1294/12

如果代码可以在您的表格中加以标记,您也可以使用DISTINCT

SELECT *
FROM Campaigns
WHERE name IN(
          SELECT campaign_name
          FROM campaign_tags
          GROUP BY campaign_name
          HAVING
            COUNT(DISTINCT CASE
                    WHEN tag IN("sports","life","stackoverflow","health","programming")
                    THEN tag
                  END)=COUNT(DISTINCT tag)
        )

SQL小提琴 - http://sqlfiddle.com/#!9/ed1294/13

答案 2 :(得分:0)

很抱歉这个解决方案仅针对sqlserver,未能看到它是针对mysql的。但是会留下对其他人有用的东西。 Charindex可以用Locate函数替换,但是mysql不支持WITH

该解决方案会查找搜索字符串中包含所有标记的所有广告系列

此解决方案可以轻松放置您想要搜索的值。

唯一需要注意的是,没有标签可以包含','。如果发生这种情况,则需要另一个分隔符。 。也不会在搜索字符串中容纳重复的标签。

对于记录,使用campaign_id可以更好地进行连接

create table c
    (
        thename varchar(100),
        thedate datetime,
        theurl  varchar(100)
    )

create table ctags
(
    thename varchar(100),
    thetag  varchar(100)
)

create unique index c_u1 on c(thename)
create unique index ctags_u1 on ctags(thename,thetag)


insert into c values ('mycampaign_good1','20170101','http://127.0.0.1')
insert into c values ('mycampaign_bad1','20170101','http://127.0.0.1')

insert into ctags values ('mycampaign_good1','sports')
insert into ctags values ('mycampaign_good1','stackoverflow')
insert into ctags values ('mycampaign_good1','programming')

insert into ctags values ('mycampaign_bad1','sports')
insert into ctags values ('mycampaign_bad1','stackoverflow')
insert into ctags values ('mycampaign_bad1','life')

/* only good1 succeeds*/
with searchstr as
(
   select 'programming,sports,stackoverflow' str
),
numvals as
(
    select len(searchstr.str) - len(replace(searchstr.str,',',''))+1 n
    from searchstr
)
select  c.thename
from c join ctags on c.thename = ctags.thename
where charindex (','+ctags.thetag,','+(select str from searchstr)) >0
group by c.thename 
having count(*) = (select numvals.n from numvals)
;
/* both succeed */
with searchstr as
(
   select 'sports,stackoverflow' str
),
numvals as
(
    select len(searchstr.str) - len(replace(searchstr.str,',',''))+1 n
    from searchstr
)
select  c.thename
from c join ctags on c.thename = ctags.thename
where charindex (','+ctags.thetag,','+(select str from searchstr)) >0
group by c.thename 
having count(*) = (select numvals.n from numvals)
;
/* both fail as neither have friends */
with searchstr as
(
   select 'friends,sports,stackoverflow' str
),
numvals as
(
    select len(searchstr.str) - len(replace(searchstr.str,',',''))+1 n
    from searchstr
)
select  c.thename
from c join ctags on c.thename = ctags.thename
where charindex (','+ctags.thetag,','+(select str from searchstr)) >0
group by c.thename 
having count(*) = (select numvals.n from numvals)