簡體   English   中英

Postgres獨家標簽搜索

[英]Postgres exclusive tag search

我正在嘗試返回與與所有查詢的“標簽”相關聯的用戶相關聯的所有行。 我的表結構和所需的輸出如下:

admin.tags:
user_id |   tag   |   detail   |    date
   2    |  apple  | blah...    | 2015/07/14
   3    |  apple  | blah.      | 2015/07/17
   1    |  grape  | blah..     | 2015/07/23
   2    |  pear   | blahblah   | 2015/07/23
   2    |  apple  | blah, blah | 2015/07/25
   2    |  grape  | blahhhhh   | 2015/07/28 

system.users:
id  |    email
 1  | joe@test.com
 2  | jane@test.com
 3  | bob@test.com

queried tags:
'apple', 'pear'

desired output:
user_id |   tag   |   detail   |    date    |  email
   2    |  apple  | blah...    | 2015/07/14 | jane@test.com
   2    |  pear   | blahblah   | 2015/07/23 | jane@test.com
   2    |  apple  | blah, blah | 2015/07/25 | jane@test.com

由於user_id 2與“ apple”和“ pear”都相關聯,因此返回了她的“ apple”和“ pear”中的每一行,因此將它們添加到system.users中,以便還返回她的電子郵件。

我對如何正確設置此postgresql查詢感到困惑。 我已經用左反聯接進行了幾次嘗試,但是似乎無法獲得預期的結果。

派生表中的查詢將為您提供具有所有指定標簽的用戶的用戶ID,外部查詢將為您提供詳細信息。

select * 
from "system.users" s
join "admin.tags" a on s.id = a.user_id
join (
    select user_id 
    from "admin.tags" 
    where tag in ('apple', 'pear')
    group by user_id 
    having count(distinct tag) = 2
) t on s.id = t.user_id;

請注意,此查詢將包括同時具有您要搜索的兩個標簽的用戶,但也可能具有其他條件,只要它們至少指定了兩個。

使用您的樣本數據,輸出將是:

| id |         email | user_id |   tag |     detail |                   date | user_id |
|----|---------------|---------|-------|------------|------------------------|---------|
|  2 | jane@test.com |       2 | grape |   blahhhhh | July, 28 2015 00:00:00 |       2 |
|  2 | jane@test.com |       2 | apple | blah, blah | July, 25 2015 00:00:00 |       2 |
|  2 | jane@test.com |       2 |  pear |   blahblah | July, 23 2015 00:00:00 |       2 |
|  2 | jane@test.com |       2 | apple |    blah... | July, 14 2015 00:00:00 |       2 |

如果要用grape排除行,只需在外部查詢中添加一個where tag in ('apple', 'pear')

如果只希望只搜索標簽的用戶,而不希望其他用戶(例如精確除法),則可以將派生表中的查詢更改為:

select user_id 
from "admin.tags" 
group by user_id
having sum(case when tag = 'apple' then 1 else 0 end) >= 1
   and sum(case when tag = 'pear' then 1 else 0 end) >= 1 
   and sum(case when tag not in ('apple','pear') then 1 else 0 end) = 0

給定您的樣本數據,這將不會返回任何內容,因為用戶2也有grape

示例SQL提琴

解決所有類型的關系划分問題的標准雙重否定方法:(我將date重命名為zdate以避免使用關鍵字作為標識符)


    -- For convenience: put search arguments into a temp table or CTE
    -- I cheat by extracting this from the admin_tags table
    -- (in fact, there should be a table with all possible tags somwhere) 
-- WITH needed_tags AS (
    -- SELECT DISTINCT tag
    -- FROM admin_tags
    -- WHERE tag IN ('apple' , 'pear' )
    -- )

    -- Even better: directly use a VALUES() as a constructor
    -- (thanks to @jpw )
WITH needed_tags(tag) AS (
    VALUES ('apple' ) , ( 'pear' )
    )
SELECT at.user_id , at.tag , at.detail , at.zdate
    , su.email
FROM admin_tags at
JOIN system_users su ON su.id = at.user_id
WHERE NOT EXISTS (
    SELECT * FROM needed_tags nt
    WHERE NOT EXISTS (
        SELECT * FROM admin_tags nx
        WHERE nx.user_id = at.user_id
        AND nx.tag = nt.tag
        )
    )
    ;

使用關聯的子選擇來計算用戶的不同標簽的數量,使用不關聯的子選擇來計算不同標簽的數量:

select at.user_id, at.tag, at.detail, at.date, su.email
from admin.tags at
  join system.users su on at.user_id = su.id
where (select count(distinct tag) from admin.tags at2
       where at2.user_id = at.user_id)
    = (select count(distinct tag) from admin.tag)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM