【问题标题】:SQL to LINQ to get all records that match all valuesSQL to LINQ 获取匹配所有值的所有记录
【发布时间】:2013-02-06 19:06:52
【问题描述】:

如何将此查询转换为 linq c# 代码:

SELECT k1.Ad_Id FROM KeywordAdCategories k1, KeywordAdCategories k2
WHERE k1.Keyword_Id =  (SELECT Id FROM keywords WHERE name = 'ALFA')
AND k2.Keyword_Id = (SELECT Id FROM keywords WHERE name = '145')
AND k1.Ad_Id = k2.Ad_Id

想法是根据可以为 N 的关键字的数量(在示例中为 2:ALFA 和 145)构建动态查询

谢谢

编辑:

关键字广告类别模型:

public class KeywordAdCategory
{
    [Key]
    [Column("Keyword_Id", Order = 0)]
    public int Keyword_Id { get; set; }

    [Key]
    [Column("Ad_Id", Order = 1)]
    public int Ad_Id { get; set; }

    [Key]
    [Column("Category_Id", Order = 2)]
    public int Category_Id { get; set; }
}

关键字模型:

public class Keyword
{
    // Primary properties
    public int Id { get; set; }
    public string Name { get; set; }
}

如果我有 5 个关键字,我会得到:

SELECT k1.Ad_Id FROM KeywordAdCategories k1, KeywordAdCategories k2, KeywordAdCategories k3, KeywordAdCategories k4, KeywordAdCategories k5
WHERE k1.Keyword_Id =  (SELECT Id FROM keywords WHERE name = 'KEYWORD1')
AND k2.Keyword_Id = (SELECT Id FROM keywords WHERE name = 'KEYWORD2')
AND k1.Ad_Id = k2.Ad_Id
AND k3.Keyword_Id =  (SELECT Id FROM keywords WHERE name = 'KEYWORD3')
AND k2.Ad_Id = k3.Ad_Id
AND k4.Keyword_Id =  (SELECT Id FROM keywords WHERE name = 'KEYWORD4')
AND k3.Ad_Id = k4.Ad_Id
AND k5.Keyword_Id =  (SELECT Id FROM keywords WHERE name = 'KEYWORD5')
AND k4.Ad_Id = k5.Ad_Id

【问题讨论】:

  • 你的 C# 类是什么样的?另外,您能否举例说明如果关键字的数量为 5 个 SQL 会是什么样子?我真的不明白为什么 KeywordAdCategories 表被使用了两次......可能有更好的方法来编写这个 SQL,这将导致更好的 LINQ 示例。
  • @ɹǝʇǝd 我认为 SQL 也可以重写得更好。然而,这个 SO 问题指出“更清洁”的解决方案性能较差:stackoverflow.com/questions/1054299/…
  • @Peter 我只是用更多信息编辑问题

标签: c# sql asp.net-mvc linq


【解决方案1】:

这比我第一次看到它时的想法有点棘手,所以我可能想多了。但这里有一个我相信对你有用的解决方案:

var keywordList = new List<string>();
keywordList.Add("ALFA");
keywordList.Add("145");

var results = KeywordAdCategories.Select (kac => kac.Ad_Id).Distinct()
            .Select (a => 
                new
                { 
                    AdId=a,
                    Keywords=KeywordAdCategories.Where(kac => kac.Ad_Id == a).Select(kac => kac.Keyword_Id)
                })
            .Where(ac => ac.Keywords.Intersect(Keywords.Where(kw => keywordList.Contains(kw.Name)).Select (kw => kw.Id)).Count() == keywordList.Count())
            .Select (ac => ac.AdId);

现在解释我在这里做了什么。首先,我想从所有可能的 Ad id 开始,所以我们有这行:

KeywordAdCategories.Select (kac => kac.Ad_Id).Distinct()

当然,这可能更容易从您的广告表中的不同选择中检索,但我试图只使用您定义的内容。

接下来,我将每个广告 ID 及其相关的关键字集合选择到匿名类型对象列表中:

.Select (a => 
              new
              { 
                  AdId=a,
                  Keywords=KeywordAdCategories.Where(kac => kac.Ad_Id == a).Select(kac => kac.Keyword_Id)
              })

接下来,我将结果过滤为仅包含与我们的关键字列表相交并且与我们的关键字列表具有相同数量的结果元素的关键字集合的对象。

.Where(ac => ac.Keywords.Intersect(Keywords.Where(kw => keywordList.Contains(kw.Name)).Select (kw => kw.Id)).Count() == keywordList.Count())

最后我只选择结果的广告 ID

.Select (ac => ac.AdId);

如果你有兴趣,这里是 emmited sql

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'ALFA'
DECLARE @p1 NVarChar(1000) = '145'
DECLARE @p2 Int = 2
-- EndRegion
SELECT [t1].[Ad_Id]
FROM (
    SELECT DISTINCT [t0].[Ad_Id]
    FROM [KeywordAdCategories] AS [t0]
    ) AS [t1]
WHERE ((
    SELECT COUNT(*)
    FROM (
        SELECT DISTINCT [t2].[Keyword_Id]
        FROM [KeywordAdCategories] AS [t2]
        WHERE (EXISTS(
            SELECT NULL AS [EMPTY]
            FROM [Keywords] AS [t3]
            WHERE ([t2].[Keyword_Id] = [t3].[Id]) AND ([t3].[Name] IN (@p0, @p1))
            )) AND ([t2].[Ad_Id] = [t1].[Ad_Id])
        ) AS [t4]
    )) = @p2

我确信有更优雅的方法可以完成您想做的事情,但我想看看是否可以在单个 linq 查询中完成所有操作。希望这会有所帮助。

【讨论】:

【解决方案2】:
List<string> keywordnames = new List<string>();
// populate keywordsnames list    

IQueryable<int> AdIds = from k in KeywordAdCategories
    where 
        (from kw in Keywords where keywordnames.Contains(kw.name) select kw.id)
        .Contains(k.keyword_id)
    select k.Ad_Id;

List<string> keywordnames = new List<string>();
// populate keywordsnames list   

List<int> keywordnameids = (from kw in ctx.Keywords
                            where keywordnames.Contains(kw.name)
                            select kw.id).ToList();

List<int> AdIds = (from k in ctx.KeywordAdCategories
                   where keywordnameids.Contains(k.Keyword_Id)
                   select k.Ad_Id).ToList();

【讨论】:

  • 谢谢,但您的回答是返回仅包含一个关键字的 Ad_Id,目标是获取包含所有关键字的 Ad_Id
  • @Patrick,请仔细检查关键字名称是否填充了您要查找的所有名称,并且在进行字符串比较时是否考虑了区分大小写。另外,请尝试我最近编辑的第二个示例。
  • 我做了,但即使是第二个示例仍然返回所有具有一个关键字或订单的广告,而不是两者
【解决方案3】:
public static int GetAdId(string KeywordId)
{
    var dbContext = new MyDbContext();

    var result = (from k in in dbContext.KeywordAdCategories
                   where k.Keyword_Id == KeywordId
                   select k.Ad_Id).SingleOrDefault();

    return result;
}

通过这个方法运行你所有的关键字id,你会得到对应的广告id。我建议您阅读 LINQ。一旦你了解了它,它就是一个很棒的工具,而且学习起来并不难,尽管一开始它可能看起来有点吓人。 Pluralsight.com 有很多很棒的视频,很容易掌握和学习:)

【讨论】:

  • 这不考虑AND。更像是OR
  • 将其实际放在评论中而不是仅仅对答案投反对票会更具建设性。
  • 答案是错误的,所以我两个都做了。这就是投票的重点,以确保人们意识到答案是否错误。如果它很接近,我不会投票,但如果它是完全错误的,那么我会这样做
  • 我的代码是基于给定的信息。他有一些这些关键字 id 的列表,他可以循环遍历并为每次迭代运行该方法。我不会说错的,我会称之为另一种方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-26
  • 2012-04-23
  • 1970-01-01
相关资源
最近更新 更多