nhibernate - 使用子查询进行计数

发布于 2024-11-27 23:52:41 字数 3146 浏览 0 评论 0原文

因此,我在 Specialism 和 SpecialismCombo 之间存在多对多关系。我想做的是获取 ids 的 int[] 并检查是否已经存在包含这些 ids 的专业的组合。

我很接近,但不太正确。 假设我有 Id 1 和 3 的专业,我用这些专业创建了一个组合。

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

如果我传入 1,那么它会返回同时具有 1 和 3 的组合 id。

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

所以就像我确实需要这个结果的计数,并匹配与组合相关的总专业计数。我不太明白我是否在执行子查询或分离标准,或者如何使用 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) = (select count(SpecialismId) 来自specialismComboSpecialism 其中 SpecialismComboId = y0 group by SpecialismComboId) == @p2

或者也许比这更简单,我需要排除SpecalismCombos,其中combo.specialisms不在id集合中。

IE。如果组合具有专长 1 和 3,但集合只有 1.. 那么我们可以根据 3 不在集合中排除此组合...

编辑 8/8/2011 回到关注如何在 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 领域!)

So I have a many to many relationship between something known as Specialism and SpecialismCombo. What I'm trying to do is take an int[] of ids and check if there is a combo already that contains the specialisms with those ids.

I was close but not quite right.
Say I have specialisms with Ids 1 and 3 and I create a combo with those specialisms.

If I pass in 3 & 1 then it returns the expected combo id.

If I pass in 1 then it returns the combo id that has both 1 and 3.

I can't just rely on total number of specialisms associated with the combo. Because if a combo has two items, 1 and 4 and the items being matched on are 1 and 3 I don't want this coming back as a matched combo.

So it's like I do need the count of this result, and match the count of total specialisms associated to the combo. I don't quite get whether I'm after a subquery or detatchedcriteria or how to get the result I want using nhibernate criteria. Thanks for your help!

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();

The sql being generated is:

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

EDIT - It seems like I either need the having to be something like...

HAVING count(s1_.SpecialismId) = (select count(SpecialismId)
from specialismComboSpecialism
where SpecialismComboId = y0
group by SpecialismComboId) == @p2

Or maybe it's simpler than that and I need to exclude SpecalismCombos where the combo.specialisms are not in the collection of ids.

Ie. if the combo has specialisms 1 and 3 but the collection only has 1.. then we could exclude this combo based on 3 not being in the collection…

Edit 8/8/2011
Went back to focusing on how to get the result I needed in SQL - and I believe this query works.

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

So now I just need to figure out how to call this procedure from my nhibernate code passing in the appropriate values :)

I also discovered in the process that my mapping class was wrong - as it was putting the wrong values in the mapping table (ie. the specialismid was ending up in the specialismcomboid field !)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

飘逸的'云 2024-12-04 23:52:41

您的解决方案实际上应该运行良好。专业是按 id 过滤的,不应该有任何东西没有被搜索到,所以 count 应该可以工作。除非你有同样的专长加入多一次。这个 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

Your solution should actually work well. The specialisms are filtered by id and there shouldn't be anything left that is not searched for, so count should work. Unless you have the same specialism joined more the once. This currentCriteria lastCriteria stuff looks a bit strange, may be there is an error. Just use Expression.In or Conjunction.

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()));

Should result in a query like this:

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

The same in HQL

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

You could also join the specialism as many times as you have ids to find:

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));
}

should create a query like this:

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
眼前雾蒙蒙 2024-12-04 23:52:41

因此,我最终创建了一个存储过程并使用 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

So I ended up creating a stored proc and using SQL CTE in order to get only the specialism combos with the correct count of specialisms. Posting this in case someone else comes across a similar issue.

Rediscovered after 8 months of using nhibernate that I'd forgotten a lot of SQL stuff :)

 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文