n个SQL交集

时间:2018-07-31 22:29:38

标签: c# sql entity-framework linq

给出下表:

MeasureID FilterID
--------- --------
3         1
3         2
4         1
4         2
4         3
4         4
4         5
5         1

我希望能够选择由n个度量的交集得出的过滤器ID。

Example:
MeasureID 3, 4 results in FilterID 1, 2
MeasureID 3, 4, 5 results in FilterID 1
MeasureID 4 results in FilterID 1, 2, 3, 4, 5

SQL查询会很好,我知道可以通过以下方式完成此事:

select m.FilterID from MeasureFilter m
where m.MeasureID = 3

intersect

select m.FilterID from MeasureFilter m
where m.MeasureID = 4

但是有可能选择10s或100s的度量。

最终,这将使用LINQ和Entity Framework在C#中生成。我知道这可以在某种C#循环中完成,但是提出一些声明性LINQ会很好。

有想法吗?

4 个答案:

答案 0 :(得分:1)

您可以将Aggregate与种子一起使用。

例如

var measures = new List<MeasureFilter>{
    new MeasureFilter(3,1),
    new MeasureFilter(3,2),
    new MeasureFilter(4,1),
    new MeasureFilter(4,2),
    new MeasureFilter(4,3),
    new MeasureFilter(4,4),
    new MeasureFilter(4,5),
    new MeasureFilter(5,1),
};


// all your MeasureIds that you need
var mIds = new List<int> { 3, 4, 5 };

var res = mIds.Aggregate(new List<int>(), (list, next) =>
{
    if (!list.Any()) //need to initially fill list otherwise no intersection
        return measures
            .Where(m => m.MeasureId == mIds.First())
            .Select(m => m.FilterId)
            .ToList();
    return list
        .Intersect(measures.Where(m => m.MeasureId == next)
        .Select(m => m.FilterId))
        .ToList();
});

答案 1 :(得分:1)

尝试this solution

var filterIDs = context.MeasureFilters.Where(x => measureIds.Contains(x.MeasureID))
    .Select(x => new { x.FilterID, x.MeasureID }).ToList()
    .GroupBy(x => x.MeasureID).Select(x => x.Select(y => y.FilterID))
    .Aggregate((a, b) => a.Intersect(b)).ToList();

答案 2 :(得分:0)

如果我正确理解:

select filterid
from t
where measureid in (3, 4, 5)
group by filterid
having count(*) = 3;

3子句中的havingwhere中列表的长度。

答案 3 :(得分:0)

理论

您请求的函数有点像数学归纳法证明:如果您已经计算出前N个measureIds的结果,并将其命名为F(N),则第一个(N + 1)measureIds的结果为F(N)与元素N + 1的“ FilterIds”的交集。

换句话说:一旦您为measureIds 3、4、7(假设X)计算了函数,然后measureIds 3、4、7、10的结果就是“ FilterIds”所属元素的交集到X的'measureId'10。

每当您在需求中看到某项计算的输出是下一个计算的输入以及下一个元素时,函数Queryable.Aggregate就会立即出现在您的脑海中。

>

回到您的问题

因此,您需要一个函数,将先前计算的结果作为一个参数,将下一个元素作为另一个参数:

  • 'previousResult',是FilterIds的序列。到目前为止,它是“ FilterIds”或所有已处理的measureIds的交集。
  • “ nextMeasureId”是下一个要处理的measureId

函数的输出应为'previousResult'与所有属于'measureId'的'FilterIds'的交集

(previousResult, nextMeasureId) =>
    previousResult.Intersect( "all FilterIds belonging to nextMeasureId")

现在要对此进行形式化,我需要一个类。

class MeasureResult
{
    public int MeasureId {get; set;}
    public int FilterId {get; set;}
}

我们使用输入IQueryable<MeasureResult>(这是您的原始表)创建一个函数,其他输入为IEnumerable<int> measureIds。

输出是一个int序列,其中包含这些FilterIds中所有measureIds的交集:IEnumerable<int>

我将其实现为扩展功能,因此可以用作任何linq函数。参见Extension Methods Demystified

static IEnumerable<int> IntersectFilterIds(this IQueryable<MeasureResult> measureResults,
    IEnumerable<int> measureIds)
{
    // Todo: check validity input parameters;

    // if no measureIds, return empty sequence:
    if (!measureIds.Any()) return Enumerable.Empty<int>();

    // TODO: add some Aggregate function
}

Aggregate的输入是measureId的序列:取第一个和第二个measureId并相交。取结果和第三个measureId并相交,取结果与第四个measureId并相交,等等。

“聚合”的输出是您请求的交集。这与输入序列的元素类型不同:输入元素是整数,输出元素是整数序列。因此,您需要聚合重载,其中结果(TAccumulate)与输入序列元素(TSource)的类型不同。此版本需要Seed,这是您第一个交集的结果:具有第一个元素的交集。

我们还需要一个函数,该函数获取您的measureResults表和一个measureId表,并返回此FilterIds的所有measureId。我将其写为扩展功能:

static IEnumerable<int> GetFilterIds(this IEnumerable<MeasureResult> measureResults,
     int measureId)
{
    return measureResults.Where(measureResult => measureResult.MeasureId == measureId)
        .Select(measureResult.FilterId);
}

此函数用于仅获取第一个元素的交集结果(所有元素均为该第一个元素的FilterIds。用作Seed。还用于获取下一个元素的FilterIds

现在,我们已经可以完成所有工作:

  • 种子是第一个FilterIds的{​​{1}}
  • 汇总的来源是所有其他measureId
  • 累加函数是previousResult与下一个measureIds的{​​{1}}的交集

    静态IEnumerable IntersectFilterIds(此IEnumerable measureResults,     IEnumerable measureIds) {     如果(!measureIds.Any())返回Enumerable.Empty();

    FilterIds

    }

我不确定您的measureResults和measureIds是在本地内存(IEnumerable)中还是在数据库表中。如果是后者,则不能使用函数,因此必须编写代码而不是函数,这使得所有代码都很难看懂,这就是为什么我选择使用函数进行解释。

measureId