可以改进查找缺失密钥的查询吗? (SQL或LINQ)

时间:2016-02-13 07:19:24

标签: c# sql sql-server linq

我正在开发一个ASP.NET MVC网站,并正在寻找一种方法来改进这个例程。它可以在LINQ级别或SQL Server级别进行改进。我希望充其量只能在一次查询调用中完成。

以下是涉及的表格和一些示例数据: enter image description here

我们没有约束,每个Key必须具有每个LanguageId值,实际上业务逻辑不允许这样的约束。但是,在应用程序级别,我们要警告管理员密钥缺少/某些语言值。所以我有这个类和查询:

public class LocalizationKeyWithMissingCodes
{
    public string Key { get; set; }
    public IEnumerable<string> MissingCodes { get; set; }
}

此方法获取Key列表以及任何缺失的代码(例如,如果我们有en + jp + ch语言代码,并且键只有en + ch的值,则列表将包含jp):< / p>

    public IEnumerable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes()
    {
        var languageList = Utils.ResolveDependency<ILanguageRepository>().GetActive();
        var languageIdList = languageList.Select(q => q.Id);
        var languageIdDictionary = languageList.ToDictionary(q => q.Id);

        var keyList = this.GetActive()
            .Select(q => q.Key)
            .Distinct();

        var result = new List<LocalizationKeyWithMissingCodes>();
        foreach (var key in keyList)
        {
            // Get missing codes
            var existingCodes = this.Get(q => q.Active && q.Key == key)
                .Select(q => q.LanguageId);

            // ToList to make sure it is processed at application
            var missingLangId = languageList.Where(q => !existingCodes.Contains(q.Id))
                .ToList();
            result.Add(new LocalizationKeyWithMissingCodes()
            {
                Key = key,
                MissingCodes = missingLangId
                    .Select(q => languageIdDictionary[q.Id].Code),
            });
        }

        result = result.OrderByDescending(q => q.MissingCodes.Count() > 0)
            .ThenBy(q => q.Key)
            .ToList();

        return result;
    }

我认为我当前的解决方案并不好,因为它会为每个密钥进行查询调用。有没有办法通过提高速度或在一个查询调用中打包来改进它?

编辑:这是答案的最终查询:

    public IQueryable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes()
    {
        var languageList = Utils.ResolveDependency<ILanguageRepository>().GetActive();
        var localizationList = this.GetActive();

        return localizationList
            .GroupBy(q => q.Key, (key, items) => new LocalizationKeyWithMissingCodes()
            {
                Key = key,
                MissingCodes = languageList
                    .GroupJoin(
                        items,
                        lang => lang.Id,
                        loc => loc.LanguageId,
                        (lang, loc) => loc.Any() ? null : lang)
                    .Where(q => q != null)
                    .Select(q => q.Code)
            }).OrderByDescending(q => q.MissingCodes.Count() > 0) // Show the missing keys on the top
            .ThenBy(q => q.Key);
    }

3 个答案:

答案 0 :(得分:3)

另一种可能性,使用LINQ:

public IEnumerable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes(
    List<Language> languages,
    List<Localization> localizations)
{
    return localizations
        .GroupBy(x => x.Key, (key, items) => new LocalizationKeyWithMissingCodes
        {
            Key = key,
            MissingCodes = languages
                .GroupJoin( // check if there is one or more match for each language
                    items,
                    x => x.Id,
                    y => y.LanguageId,
                    (x, ys) => ys.Any() ? null : x)
                .Where(x => x != null) // eliminate all languages with a match
                .Select(x => x.Code) // grab the code
        })
        .Where(x => x.MissingCodes.Any()); // eliminate all complete keys 
}

答案 1 :(得分:2)

这是用于识别缺少“完整”语言分配的密钥的SQL逻辑:

SELECT
    all.[Key],
    all.LanguageId
FROM
    (
    SELECT
        loc.[Key],
        lang.LanguageId
    FROM
        Language lang
    FULL OUTER JOIN
        Localization loc
        ON (1 = 1)
    WHERE
        lang.Active = 1
    ) all
LEFT JOIN
    Localization loc
    ON (loc.[Key] = all.[Key])
    AND (loc.LanguageId = all.LanguageId)
WHERE
    loc.[Key] IS NULL;

要查看所有键(而不是过滤):

SELECT
    all.[Key],
    all.LanguageId,
    CASE WHEN loc.[Key] IS NULL THEN 1 ELSE 0 END AS Flagged
FROM
    (
    SELECT
        loc.[Key],
        lang.LanguageId
    FROM
        Language lang
    FULL OUTER JOIN
        Localization loc
        ON (1 = 1)
    WHERE
        lang.Active = 1
    ) all
LEFT JOIN
    Localization loc
    ON (loc.[Key] = all.[Key])
    AND (loc.LanguageId = all.LanguageId);

答案 2 :(得分:1)

您的代码似乎正在进行大量的数据库查询和实现。

就LINQ而言,单个查询看起来像这样..

我们采用语言和本地化表的笛卡尔积来获得(键,代码)的所有组合,然后减去关系中存在的(键,代码)元组。这给了我们不存在的(密钥,代码)组合。

var result = context.Languages.Join(context.Localizations, lang => true, 
loc => true, (lang, loc) => new { Key = loc.Key, Code = lang.Code })
.Except(context.Languages.Join(context.Localizations, lang => lang.Id, 
loc => loc.LanguageId, (lang, loc) => new { Key = loc.Key, Code = lang.Code }))
.GroupBy(r => r.Key).Select(r => new LocalizationKeyWithMissingCodes 
{ 
 Key = r.Key, 
 MissingCodes = r.Select(kc => kc.Code).ToList()
})
.ToList()
.OrderByDescending(lkmc => lkmc.MissingCodes.Count())
.ThenBy(lkmc => lkmc.Key).ToList();

P.S。我随时输入了这个LINQ查询,所以请告诉我它是否有语法问题。 查询的要点是我们采用笛卡尔积并减去匹配的行。