nhibernate - 使用子查询计数

时间:2011-08-04 07:18:48

标签: c# fluent-nhibernate

所以我在Specialism和SpecialismCombo之间有很多关系。我要做的是取一个int []的id并检查是否已经有一个包含这些id的特殊主义的组合。

我很接近但不太对劲。  说我对Ids 1和I有专长,并且我用这些专业创建了一个组合。

如果我通过3& 1然后它返回预期的组合ID。

如果我传入1,则返回同时包含1和3的组合ID。

我不能仅依靠与组合相关的专业总数。因为如果一个组合有两个项目,1和4以及匹配的项目是1和3我不希望它作为匹配的组合返回。

因此我需要计算此结果,并匹配与组合相关的总专业数。我不知道我是在追查子查询还是在detatchedcriteria之后,或者如何使用nhibernate标准得到我想要的结果。谢谢你的帮助!

int[] SpecialismIds = ArrayExtensions.ConvertArray<int>(idCollection.Split(new char[] { '|' }));

    ICriteria query = m_SpecialismComboRepository.QueryAlias("sc");
        query.CreateAlias("sc.Specialisms", "s", NHibernate.SqlCommand.JoinType.InnerJoin);

    ICriterion lastCriteria = null;

    foreach(int i in SpecialismIds)
    {

         ICriterion currentCriteria = Restrictions.Eq("s.SpecialismId", i);
        if (lastCriteria != null)
                        lastCriteria = Restrictions.Or(lastCriteria, currentCriteria);
                    else
                        lastCriteria = currentCriteria;
    }

    if (lastCriteria != null)
                    query.Add(lastCriteria);

    IProjection IdCount = Projections.Count("s.SpecialismId").As("IdCount");

    query.SetProjection(
        Projections.GroupProperty("sc.SpecialismComboId"),
        IdCount 
        );

    query.Add(Restrictions.Eq(IdCount, SpecialismIds.Count()));

    var comboId = query.List();

生成的sql是:

SELECT this_.SpecialismComboId as y0_, count(s1_.SpecialismId) as y1_ 
FROM dbo.SpecialismCombo this_ 
inner join SpecialismComboSpecialism specialism3_ on this_.SpecialismComboId=specialism3_.SpecialismId 
inner join dbo.Specialism s1_ on specialism3_.SpecialismComboId=s1_.SpecialismId WHERE s1_.SpecialismId = @p0 
GROUP BY this_.SpecialismComboId HAVING count(s1_.SpecialismId) = @p1',N'@p0 int,@p1 int',@p0=3,@p1=1

编辑 - 似乎我要么必须像...那样......

HAVING count(s1_.SpecialismId)=(选择计数(SpecialismId)) 来自specialismComboSpecialism 其中SpecialismComboId = y0 group by SpecialismComboId)== @ p2

或者它可能比这更简单,我需要排除SpecalismCombos,其中combo.specialisms不在ids的集合中。

IE中。如果组合具有专家1和3,但该集合只有1 ..那么我们可以排除这个组合基于3不在集合...

编辑2011年8月8日 回过头来关注如何在SQL中获得我需要的结果 - 我相信这个查询有效。

WITH CustomQuery AS
        (
        SELECT sc.SpecialismComboId,
        count(s.SpecialismId) AS ItemCount
        FROM SpecialismCombo sc 
        inner join SpecialismComboSpecialism scs on sc.SpecialismComboId = scs.SpecialismComboId
        inner join Specialism s on s.SpecialismId = scs.SpecialismId
        GROUP BY sc.SpecialismComboId 
        HAVING count(s.SpecialismId) = 2
        )

        SELECT CustomQuery.SpecialismComboId FROM CustomQuery
        INNER JOIN SpecialismComboSpecialism scs on CustomQuery.SpecialismComboId = scs.SpecialismComboId
        WHERE scs.SpecialismId in (1,4)
        GROUP BY CustomQuery.SpecialismComboId 
        HAVING count(scs.SpecialismId) = 2

所以现在我只需要弄清楚如何从我的nhibernate代码调用此过程传递适当的值:)

我还在流程中发现我的映射类是错误的 - 因为它在映射表中放入了错误的值(即,specialismid最终出现在specialismcomboid字段中!)

2 个答案:

答案 0 :(得分:0)

您的解决方案实际上应该运作良好。专业按ID过滤,不应该有任何未被搜索的内容,因此计数应该有效。除非你有同样的专业,否则加入一次。这个currentCriteria lastCriteria的东西看起来有点奇怪,可能有错误。只需使用Expression.InConjunction

IProjection IdCount = Projections.Count("s.SpecialismId").As("IdCount");

IQuery query = session
  .CreateCriteria<SpecialismCombo>("sc")
  .CreateCriteria("Specialism", "s");

  .Add(Expression.In("s.SpecialismId", SpecialismIds));

  .SetProjection(
    Projections.GroupProperty("sc.SpecialismComboId"),
    IdCount);

  .Add(Restrictions.Eq(IdCount, SpecialismIds.Count()));

应该导致这样的查询:

select ...
from 
  SpecialismCombo sc 
  inner join -- linktable ...
  inner join Specialism s on ...
where
  s.SpecialismId in (1, 4)
Group By sc.SpecialismComboId
having count(*) = 2

在HQL中相同

from SpecialismCombo sc 
  join sc.Specialism s
where s.id in (:ids)
group by sc
having count(*) = :numberOfIds

您也可以多次加入专业,因为您有ID可以找到:

IQuery query = session.CreateCriteria<SpecialismCombo>("sc")

int counter = 0;
foreach(int id in ids)
{
  string alias = "s" + counter++;
  query
    .CreateCriteria("Specialism", alias);
    .Add(Expression.Eq(alias + ".SpecialismId", id));
}

应该创建一个这样的查询:

select ...
from 
  SpecialismCombo sc 
  inner join -- linktable ...
  inner join Specialism s0 on ...
  inner join -- linktable ...
  inner join Specialism s1 on ...
where
  s0.SpecialismId = 1
  and s1.SpecialismId = 4

答案 1 :(得分:0)

所以我最终创建了一个存储过程并使用SQL CTE,以便只获得具有正确计数专业的专业组合。发布此信息以防其他人遇到类似问题。

使用nhibernate 8个月后重新发现,我忘记了很多SQL的东西:)

 DECLARE @IdCollectionCount         INT
    , @IdCollection             VARCHAR(250)
    , @CollectionDelimiter      NVARCHAR

    SET @IdCollectionCount = 2;
    SET @IdCollection = '1,4';
    SET @CollectionDelimiter= ',';

    WITH CustomQuery AS
        (
        SELECT sc.SpecialismComboId,
        count(s.SpecialismId) AS ItemCount
        FROM SpecialismCombo sc 
        inner join SpecialismComboSpecialism scs on sc.SpecialismComboId = scs.SpecialismComboId
        inner join Specialism s on s.SpecialismId = scs.SpecialismId
        GROUP BY sc.SpecialismComboId 
        HAVING count(s.SpecialismId) = @IdCollectionCount
        )

        SELECT Top 1 CustomQuery.SpecialismComboId FROM CustomQuery
        INNER JOIN SpecialismComboSpecialism scs on CustomQuery.SpecialismComboId = scs.SpecialismComboId
        INNER JOIN dbo.fn_SplitDelimited(@IdCollection,@CollectionDelimiter) AS ids
              ON scs.SpecialismId = CAST(ids.ListValue AS INT)
        GROUP BY CustomQuery.SpecialismComboId 
        HAVING count(scs.SpecialismId) = @IdCollectionCount