SQL 中的所有/任意实现以及 IN

问题描述 投票:0回答:2

您好,我需要通过 dapper 在类似于下面的伪代码的结构上实现一些查询。

// Tags
[{id: 1, name: "Tech"}, {id: 2, name: "SQL"}, {id: 3, name: "C#"}]

// BlogPost [
{
  Id: 1
  Tags: [1, 2] // Tech, Sql
},
{
   Id: 2,
   Tags: [1,3] // Tech, C#  
},
{
   Id: 3,
   Tags: [1,2,3] // Text, Sql, C#
}]

鉴于此查询

SELECT 
    [Blogpost].*
From BlogPost blogPost
    LEFT JOIN BlogPostTags tags ON tags.blogId = blogPost.Id
WHERE blogpost.Tags IN (1,2)

运行上面的查询我希望得到这个结果。 [{博客ID:1},{博客ID:2},{博客ID:3}]

  • 在上面的查询中给定相同的参数(1,2),我需要获得类似于 [{blogId: 1}] 的结果。

  • 我还需要根据上面查询中的参数 (1,2) 获得像 post [{blogId: 1}, {BlogId: 3}] 这样的结果。

sql(MSSQL)有什么好方法来获得这些结果吗?

或者我怎样才能以高性能的方式获得这些结果,因为我在实际查询中会有更多的连接?

谢谢!

sql sql-server dapper relational-division
2个回答
0
投票

这些类型的查询在 SQL 中表达起来非常尴尬(注意:我会避免在这里使用

LEFT OUTER JOIN
,因为这可能会导致行重复)

“包含任何”是可以的(通常通过

EXISTS (SELECT 1 FROM BlogPostTags tags WHERE tags.blogId = blogPost.Id AND tags.Tags IN @tags)
,使用 Dapper 的自动扩展
@tags
) - 但是,您要求的是“包含所有”和“仅包含所有”;这些要复杂得多; “包含全部”通常是多个
EXISTS
操作,而“仅包含全部”可以是相同的“包含全部”,并附加一个
NOT EXISTS (SELECT 1 FROM BlogPostTags tags WHERE tags.blogId = blogPost.Id AND tags.Tags NOT IN @tags)

但是!我再次强调:这个操作在 SQL 中并不理想。对于 Stack Overflow 帖子标记(看起来与此基本相同),我们通过添加一个单独的索引/服务器(纯粹为了执行标记操作)而存在,包括将所有帖子/标记数据预加载到 RAM 中,从而彻底改变了这一点以优化的方式,并按标签建立索引(处理增量更新);这允许使用各种技巧在内存中执行大量操作。比简单的 SQL 查询要付出更多的努力和工作,而且效率也更高。 “包含任何”只是一个简单的

EXISTS

0
投票

var tagIds = new[]{ 1, 2, }; using connection = GetConnection(); const string query = @" SELECT bp.* FROM BlogPost bp WHERE EXISTS (SELECT 1 FROM BlogPostTags tags WHERE tags.blogId = bp.Id AND tags.Id IN @tags ); var results = await connection.QueryAsync<BlogPost>(query, new { tags, });

另外两个要求,

这些是经典的
关系除法

,一个是With Remainder,另一个是Without Remainder。 对于 With Remainder,只需执行 EXISTS 连接,并进行分组

HAVING

检查它们是否全部存在。

var tagIds = new[]{ 1, 2, };

using connection = GetConnection();
const string query = @"
SELECT 
    bp.*
FROM BlogPost bp
WHERE EXISTS (SELECT 1
    FROM BlogPostTags tags
    WHERE tags.blogId = bp.Id
      AND tags.Id IN @tags
    HAVING COUNT(*) = @count
);

var results = await connection.QueryAsync<BlogPost>(query, new { tags, count = tags.Length });
对于 Without Remainder,它是类似的,但是您需要查找 

all
 标签,然后检查 
HAVING

是否匹配的 only 就是您要查找的标签。

var tagIds = new[]{ 1, 2, };

using connection = GetConnection();
const string query = @"
SELECT 
    bp.*
FROM BlogPost bp
WHERE EXISTS (SELECT 1
    FROM BlogPostTags tags
    WHERE tags.blogId = bp.Id
    HAVING COUNT(*) = @count
       AND COUNT(*) = COUNT(CASE WHEN tags.Id IN @tags THEN 1 END)
);

var results = await connection.QueryAsync<BlogPost>(query, new { tags, count = tags.Length });

请注意,Dapper 自动将列表参数化为 

(@p1, @p2)
 等。

您还可以使用表值参数,这对于大型列表更有效。

定义一个表类型,最好保留一些有用的标准类型。

CREATE TYPE dbo.IntList AS TABLE (value int PRIMARY KEY);

然后将值放入

DataTable
 并使用 
.AsTableValuedParameter

查询也略有不同,因为您将在此处使用联接。
var tagIds = new[]{ 1, 2, };
var table = new DataTable { Columns = { { "value", typeof(int) } } };
foreach (var tag in tagIds)
    table.Add(tag);

using connection = GetConnection();
const string query = @"
SELECT 
    bp.*
FROM BlogPost bp
WHERE EXISTS (SELECT 1
    FROM BlogPostTags tags
    LEFT JOIN @tags input ON input.value = tags.Id
    WHERE tags.blogId = bp.Id
    HAVING COUNT(*) = @count
       AND COUNT(*) = COUNT(input.Tag)
);

var results = await connection.QueryAsync<BlogPost>(query, new { tags = tags.AsTableValuedParameter("dbo.IntList"), count = tags.Length });

© www.soinside.com 2019 - 2024. All rights reserved.