【问题标题】:SQL query that gives distinct results that match multiple columns提供与多列匹配的不同结果的 SQL 查询
【发布时间】:2010-11-15 05:13:35
【问题描述】:

抱歉,我无法为我的问题提供更好的标题,因为我对 SQL 还很陌生。 我正在寻找解决以下问题的 SQL 查询字符串。

假设如下表:

DOCUMENT_ID |标签 ---------------------------- 1 |标签1 1 |标签2 1 |标签3 2 |标签2 3 |标签1 3 |标签2 4 |标签1 5 |标签3

现在我想选择所有包含一个或多个标签的不同文档 ID(但必须提供所有指定的标签)。 例如: 选择所有带有 tag1 和 tag2 的 document_id 将返回 1 和 3(但不是 4,例如因为它没有 tag2)。

最好的方法是什么?

问候, 启

【问题讨论】:

  • H2,但我不想使用任何 db 特定的东西。
  • 如果您使用的是 MSSQL 2005/2008,我将建议您使用公用表表达式。使用 CTE,您可以连接标签值并在结果表上使用子字符串标准。有非数据库特定的方法来进行连接,但它们使用起来过于困难。约翰的回答是很好的干净解决方案。
  • 您能否告诉我们您是否允许相同的 DocumentID + Tag 组合多次存在?
  • 数据库没有在这些列上使用这样的索引(这是一个 ORM 生成的),所以我们假设是的。

标签: sql database h2


【解决方案1】:
SELECT document_id
FROM table
WHERE tag = 'tag1' OR tag = 'tag2'
GROUP BY document_id
HAVING COUNT(DISTINCT tag) = 2

编辑:

因缺乏约束而更新...

【讨论】:

  • 确保使用您要查找的正确数量的标签调整最后一行。 IE。如果您正在寻找那些标记有 tag1、tag2 和 tag3 的文档,则需要使用 HAVING COUNT(document_id) >= 3
  • 除了使用或支持 in,这就是我想出的(你先到那里)。 +1。哦,只要你不被骗,你就不用担心 >= vs =
  • -1 如果数据中允许 Document_ID 和 Tag 重复,则上述方法无效。两行 (10, tag1) 将返回 10,即使没有行有 (10, tag2)
  • @Shannon - 这是最近才作为问题添加到 cmets 中的......大多数人正确地使用了使用约束
  • 使最后一行 HAVING COUNT(DISTINCT tag) = 2 将适用于 SQL Server 和 Oracle。
【解决方案2】:

这假定 DocumentID 和 Tag 是主键。

编辑:将 HAVING 子句更改为计数 DISTINCT 标记。这样,主键是什么就无关紧要了。

测试数据

-- Populate Test Data
CREATE TABLE #table (
  DocumentID varchar(8) NOT NULL, 
  Tag varchar(8) NOT NULL
)

INSERT INTO #table VALUES ('1','tag1')
INSERT INTO #table VALUES ('1','tag2')
INSERT INTO #table VALUES ('1','tag3')
INSERT INTO #table VALUES ('2','tag2')
INSERT INTO #table VALUES ('3','tag1')
INSERT INTO #table VALUES ('3','tag2')
INSERT INTO #table VALUES ('4','tag1')
INSERT INTO #table VALUES ('5','tag3')

INSERT INTO #table VALUES ('3','tag2')  -- Edit: test duplicate tags

查询

-- Return Results
SELECT DocumentID FROM #table
WHERE Tag IN ('tag1','tag2')
GROUP BY DocumentID
HAVING COUNT(DISTINCT Tag) = 2

结果

DocumentID
----------
1
3

【讨论】:

  • HAVING COUNT(*) = 2 而不是 >= 2 将排除具有多个给定 DocumentID 和 Tag 实例的文档,假设数据规则允许这样做。
  • 是的。这就是为什么我假设主键(或唯一键)是 DocumentID 和 Tag。否则,正如您所建议的,更改 HAVING COUNT(*) >= 2 将说明这一点。
  • 如果 dup 标签是合法的,那么整个事情就会崩溃,因为您只计算标签,而不是不同的标签。
  • 非常好的点 BCS,dup 标签会破坏海滩和约翰的答案。
  • 好的,更改实现以考虑重复标签(假设主键不是 DocumentID,Tag)。 HAVING COUNT(Distint Tag) = 2 现在应该适用于所有情况。
【解决方案3】:
select DOCUMENT_ID
      TAG in ("tag1", "tag2", ... "tagN")
   group by DOCUMENT_ID
   having count(*) > N and 

根据需要调整N和标签列表。

【讨论】:

    【解决方案4】:
    Select distinct document_id 
    from {TABLE} 
    where tag in ('tag1','tag2')
    group by id 
    having count(tag) >=2 
    

    如何在 where 子句中生成标签列表取决于您的应用程序结构。如果您将查询作为代码的一部分动态生成,那么您可以简单地将查询构造为动态生成的大字符串。

    我们总是使用存储过程来查询数据。在这种情况下,我们将标签列表作为 XML 文档传入。 - 像这样的过程可能看起来像其中一个输入参数是

    <tags>
       <tag>tag1</tag>
       <tag>tag2</tag>
    </tags>
    
    
    CREATE PROCEDURE [dbo].[GetDocumentIdsByTag]
    @tagList xml
    AS
    BEGIN
    
    declare @tagCount int
    select @tagCount = count(distinct *) from @tagList.nodes('tags/tag') R(tags)
    
    
    SELECT DISTINCT documentid
    FROM {TABLE}
    JOIN @tagList.nodes('tags/tag') R(tags) ON {TABLE}.tag = tags.value('.','varchar(20)')
    group by id 
    having count(distict tag) >= @tagCount 
    
    END
    

    CREATE PROCEDURE [dbo].[GetDocumentIdsByTag]
    @tagList xml
    AS
    BEGIN
    
    declare @tagCount int
    select @tagCount = count(*) from @tagList.nodes('tags/tag') R(tags)
    
    
    SELECT DISTINCT documentid
    FROM {TABLE}
    WHERE tag in
    (
    SELECT tags.value('.','varchar(20)') 
    FROM @tagList.nodes('tags/tag') R(tags)
    }
    group by id 
    having count( distinct tag) >= @tagCount 
    END
    

    结束

    【讨论】:

    • 这是错误的。 in ('tag1','tag2') 将返回 1、2、3 和 4。他说他只想要返回两个标签的 ID。
    • XML 文档,嗯?我自己还处于石器时代,具有拆分功能和逗号。先生,就像你的吊臂被割断一样。
    • 错过了同时拥有两个标签的要求
    • 添加了满足所有标签要求的条款
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-11
    • 1970-01-01
    • 2016-02-22
    • 2015-10-22
    相关资源
    最近更新 更多