如何跨多行查询匹配值?

时间:2017-02-10 19:43:01

标签: sql postgresql

我有2个表:通过player_tags加入的玩家和标签。我想让玩家Aaron只有匹配1个或更多标签名称。在我的情况下,我希望所有玩家都有标记" test"和"新"但由于它们属于两排,我不知道该怎么做。

table: players
ID    NAME
----- --------
1     AARON
2     JAMES


table: player_tags
ID    PLAYER_ID TAG_ID
----- --------- --------
1     1         1
2     1         2
3     2         1


table: tags
ID    NAME
----- --------
1     test
2     new

我创建了这个查询当然不起作用,因为标签'测试'并标记' new'分为2个不同的行。

SELECT "players".name, "tags".name FROM "players"
  INNER JOIN "player_tags" ON "player_tags"."player_id" = "players"."id" 
  INNER JOIN "tags" ON "tags"."id" = "player_tags"."tag_id"
WHERE
  "tags"."name" IS NOT NULL AND "tags"."name" = 'test' AND "tags"."name" = 'new'

我希望将播放器1作为响应,因为它匹配两个标签。如果我改变了' AND'到'或者'好吧,我得到了两个球员,但这不是我想要的。我使用postgres BTW。

4 个答案:

答案 0 :(得分:4)

select p.name as player_name, string_agg(t.name, ', ') as tags
from players p
  join player_tags pt ON pt.player_id = p.id 
  join tags t ON t.id = pt.tag_id
where t.name in ('new', 'test')
group by p.name
having count(t.id) > 1

上面实现了“如果它匹配一个或多个标签名称”部分,那么它将返回那些至少其中一个标签的玩家。

如果您只想要那些拥有这两个标签的玩家,您可以使用:

select p.name as player_name, string_agg(t.name, ', ') as tags
from players p
  join player_tags pt ON pt.player_id = p.id 
  join tags t ON t.id = pt.tag_id
group by p.name
having array_agg(t.name order by t.name) = array['new', 'test']

请注意,您需要按字母顺序指定数组中的标记名称。

在线示例:Playground Example

答案 1 :(得分:2)

试试这个,我还没有postgres进行测试,告诉我是否存在语法错误。

ARRAY['test', 'new']中,您可以添加所需的标记名称。

select player, array_agg(tag) as tags from (
    SELECT players.name AS player, tags.name AS tag FROM players
    INNER JOIN player_tags
    ON players.ID = player_tags.PLAYER_ID 
    INNER JOIN tags
    ON player_tags.TAG_ID = tags.ID
) t
group by player
having
ARRAY(SELECT unnest(array_agg(tag)) ORDER BY 1)  = ARRAY(SELECT unnest(ARRAY['test', 'new']) ORDER BY 1) 

答案 2 :(得分:1)

我对postgresql不熟悉,但是你可以为你正在寻找的每个标签加入一次播放器标签表吗?

在SQL Server中,我可以执行以下操作:

select p.name
from players p 
join player_tags pt1 on p.id = pt1.player_id and pt1.tag_id = 1
join player_tags pt2 on pt1.player_id = pt2.player_id and pt2.tag_id = 2

你能做那样的事吗?

答案 3 :(得分:1)

您必须确保两个条件都成立:

with ctag as
(
    select pt.player_id pid, t.name tname
    from players_tags pt 
         inner join tags t on t.id = pt.tag_id
    where tags.name in ('test','new')
)
select p.id, p.name
from players p
where exists (select 1 from ctag where pid = p.id and tname = 'test')
      and exists (selet 1 from ctag where pid = p.id and tname = 'new');