【问题标题】:Oracle SQL Query - Element containing every element in subqueryOracle SQL 查询 - 包含子查询中每个元素的元素
【发布时间】:2021-04-23 20:08:32
【问题描述】:

我有 3 张这样的桌子:

Document(ID:integer, Title:string)
Keywords(ID:integer, Name:string)
Document_Keywords(DocumentID:integer, KeywordID:integer)

Document_Keywords.DocumentID referencing Document.ID
Document_Keywords.KeywordID referencing Keywords.ID

一个文档包含 [0, n] 个关键字。

我想获取每个文档,其中的关键字至少包含一组另一个文档的关键字。因此:

Foo, Bar and Fred-> Documents
Foo's keywords: {1, 2, 3}
Bar's keywords: {1, 2, 3, 4}
Fred's keywords: {1, 3, 5}

如果我们搜索所有包含 Foo 关键字的文档关键字,我们会得到 Bar 而不是 Fred。

这是我目前的查询:

SELECT KeywordID
FROM Document_Keywords DK
JOIN Document D ON D.ID = DK.DocumentID
WHERE D.title = 'Foo'
MINUS
SELECT KeywordID
FROM Document_Keywords
WHERE DocumentID = 1;

如果带有 ID = 1 关键字的 Document 至少包含 Foo 的每个关键字,则返回一个空表。

我找不到任何其他方法来解决这个问题,因为我只能使用 Oracle SQL 来回答它。

【问题讨论】:

  • “关键字的子集”相当模糊。请澄清问题。
  • 乍一看,'Foo' 旁边的分号是错字,应该删除。
  • 修正查询拼写错误并举例说明子集背后的含义

标签: sql database oracle


【解决方案1】:

如果要获取文档的关键字:

SELECT KeywordID, D1.ID DOC_ID, D1.Title
FROM Document_Keywords DK1
     JOIN Document D1
        on DK1.DocumentID = D1.ID
WHERE exists
   (select 1
    from Document D2
    join Document_Keywords DK2
        on D2.ID = DK2.DocumentID
    where  D2.title = 'Foo'
     and DK1.KeywordID=DK2.KeywordID
     and D1.ID!= D2.ID
     );

带有测试数据和结果的完整测试用例:

with 
 Document(ID, Title) as (
    select 1, 'Foo' from dual union all
    select 2, 'Bar' from dual union all
    select 3, 'Fred' from dual
 )
,Keywords(ID, Name) as (
    select level, 'Key'||level from dual connect by level<=5
 )
,Document_Keywords(DocumentID, KeywordID) as (
    select 1, column_value from table(sys.odcinumberlist(1,2,3))   union all -- Foo's keywords: {1, 2, 3}
    select 2, column_value from table(sys.odcinumberlist(1,2,3,4)) union all -- Bar's keywords: {1, 2, 3, 4}
    select 3, column_value from table(sys.odcinumberlist(1,3,5))             -- Fred's keywords: {1, 3, 5}
 )
SELECT KeywordID, D1.ID DOC_ID, D1.Title
FROM Document_Keywords DK1
     JOIN Document D1
        on DK1.DocumentID = D1.ID
WHERE exists
   (select 1
    from Document D2
    join Document_Keywords DK2
        on D2.ID = DK2.DocumentID
    where  D2.title = 'Foo'
     and DK1.KeywordID=DK2.KeywordID
     and D1.ID!= D2.ID
     );

 KEYWORDID     DOC_ID TITLE
---------- ---------- -----
         1          2 Bar
         1          3 Fred
         2          2 Bar
         3          2 Bar
         3          3 Fred

如果您想要没有文档,只需列出关键字:

SELECT distinct KeywordID
FROM Document_Keywords DK1
WHERE exists
   (select 1
    from Document D2
    join Document_Keywords DK2
        on D2.ID = DK2.DocumentID
    where  D2.title = 'Foo'
     and DK1.KeywordID=DK2.KeywordID
     and DK1.DocumentID!= D2.ID
     );

带有结果的完整测试用例:

with 
 Document(ID, Title) as (
    select 1, 'Foo' from dual union all
    select 2, 'Bar' from dual union all
    select 3, 'Fred' from dual
 )
,Keywords(ID, Name) as (
    select level, 'Key'||level from dual connect by level<=5
 )
,Document_Keywords(DocumentID, KeywordID) as (
    select 1, column_value from table(sys.odcinumberlist(1,2,3))   union all -- Foo's keywords: {1, 2, 3}
    select 2, column_value from table(sys.odcinumberlist(1,2,3,4)) union all -- Bar's keywords: {1, 2, 3, 4}
    select 3, column_value from table(sys.odcinumberlist(1,3,5))             -- Fred's keywords: {1, 3, 5}
 )
SELECT distinct KeywordID
FROM Document_Keywords DK1
WHERE exists
   (select 1
    from Document D2
    join Document_Keywords DK2
        on D2.ID = DK2.DocumentID
    where  D2.title = 'Foo'
     and DK1.KeywordID=DK2.KeywordID
     and DK1.DocumentID!= D2.ID
     );


 KEYWORDID
----------
         1
         2
         3

【讨论】:

    【解决方案2】:

    如果我有这个权利,您希望文档的关键字包含所有 Fred 的关键字作为submultiset

    设置(以 Sayan 为例):

    create or replace type number_tt as table of number;
    
    create table documents(id, title) as
        select 1, 'Foo' from dual union all
        select 2, 'Bar' from dual union all
        select 3, 'Fred' from dual;
    
    create table document_keywords(documentid, keywordid) as
        select 1, column_value from table(number_tt(1,2,3))   union all
        select 2, column_value from table(number_tt(1,2,3,4)) union all
        select 3, column_value from table(number_tt(1,3,5))
    

    查询:

    with document_keywords_agg(documentid, title, keywordlist, keywordids) as (
        select d.id, d.title
             , listagg(dk.keywordid, ', ') within group (order by dk.keywordid)
             , cast(collect(dk.keywordid) as number_tt)
        from   documents d
               join document_keywords dk on dk.documentid = d.id
        group by d.id, d.title
      )
    select dk1.documentid, dk1.title, dk1.keywordlist
         , dk2.title as subset_title
         , dk2.keywordlist as subset_keywords
    from   document_keywords_agg dk1
           join document_keywords_agg dk2
                on dk2.keywordids submultiset of dk1.keywordids
    where  dk2.documentid <> dk1.documentid;
    

    结果:

    DOCUMENTID TITLE KEYWORDLIST SUBSET_TITLE SUBSET_KEYWORDS
    2 Bar 1, 2, 3, 4 Foo 1, 2, 3

    为了稍微扩展示例,让我们添加另一个包含关键字 {1,3,5,9} 的文档“Dino”:

    insert all
        when rownum = 1 then into documents values (docid, 'Dino')
        when 1=1 then into document_keywords values (docid, kw)
    select 4 as docid, column_value as kw from table(number_tt(1,3,5,9));
    

    现在结果是:

    DOCUMENTID TITLE KEYWORDLIST SUBSET_TITLE SUBSET_KEYWORDS
    2 Bar 1, 2, 3, 4 Foo 1, 2, 3
    4 Dino 1, 3, 5, 9 Fred 1, 3, 5

    (如果您只想检查一个文档,请在 where 子句中添加过滤器。)

    SQL Fiddle

    【讨论】:

      【解决方案3】:

      那么,在 KeywordID 上将 Document_Keyword 内部连接到自身可为您提供所需的原材料,不是吗?

      . . .
      From Document_Keywords A Inner Join Document_Keywords B On A.KeywordID=B.KeywordID
                                                             And A.DocumentID<>B.DocumentID
      . . .
      

      当然,如果同一个关键字出现在多个其他文档中,您将获得多次出现的 A.*,但您可以使用 Group By 或 Distinct 子句来总结这些关键字。

      如果您需要 text-y 结果,您可以在表 A 键上添加文档和关键字表连接。

      以您上面指定的格式提供结果的查询将是:

      Select Title, ListAgg(KeywordID,',') Within Group (Order By KeywordID) as KeyWord_IDs
      From (
            Select D.Title,D.ID,A.KeywordID
            From Document_Keywords A Inner Join Document_Keywords B On A.KeywordID=B.KeywordID
                                                                    And A.DocumentID<>B.DocumentID
                 Inner Join Document D on D.ID=A.DocumentID
            Group By A.DocumentID,A.KeyWordID
            )
      Group By Title,ID
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-03
        • 1970-01-01
        • 1970-01-01
        • 2019-07-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多