NHibernate QueryOver CASE WHEN 在列值上计算

4

我一直在尝试使用NHibernate QueryOver完成以下T-SQL,但没有成功:

SELECT Id, SUM(CASE MyValue WHEN 1 THEN Volume ELSE Volume * -1 END)
FROM MyTable
GROUP BY Id

我试图汇总所有的音量,但对于MyValue=1应该是正值否则为负值。到目前为止,我得到了以下内容:
 var result = this.Session.QueryOver<MyTable>()
    .Select(Projections.Group<MyTable>(x => x.Id),
    Projections.Conditional(Restrictions.Eq(Projections.Property<MyTable>(x
        => x.MyValue), '1'),
    Projections.Property<MyTable>(x => x.Volume),
    Projections.Property<MyTable>(x => x.Volume * -1)))
    .List();

但是,正如你所想象的那样,NHibernate不知道列 Volume * -1,那么我该如何在 CASE 语句中进行此计算呢?

1个回答

5
我认为这样做应该可以解决问题:
session.QueryOver<MyTable>()
    .Select(
        Projections.Group<MyTable>(x => x.Id),
        Projections.Sum(
            Projections.Conditional(
                Restrictions.Eq(
                    Projections.Property<MyTable>(x => x.MyValue), 1),
                Projections.Property<MyTable>(x => x.Volume),
                Projections.SqlFunction(
                    new VarArgsSQLFunction("(", "*", ")"),
                    NHibernateUtil.Int32,
                    Projections.Property<MyTable>(x => x.Volume),
                    Projections.Constant(-1)))))
    .List<object[]>();

作为一条规则,QueryOver在进行算术运算时表现得相当糟糕。据我所知,您必须使用VarArgsSQLFunction来构建乘法表达式。
这会生成以下SQL语句:
SELECT
    this_.Id as y0_,
    sum((
        case when this_.MyValue = 1 
        then this_.Volume else (this_.Volume*-1) end
    )) as y1_
FROM        
    MyTable this_     
GROUP BY        
    this_.Id

请注意,在此处您需要使用与自定义DTO配对的结果转换器,或者使用.List<object[]>,它将结果集转换为List of object[],每个List中的项目都是一个结果行。你不能仅使用.List(),因为NHibernate期望选择整个MyTable行,而在这里你并没有这样做。
您可能认为这很丑陋,我也同意。您可以通过将投影重构为它们自己的变量来进行一些清理:
IProjection multiplicationProjection = 
    Projections.SqlFunction(
        new VarArgsSQLFunction("(", "*", ")"),
        NHibernateUtil.Int32,
        Projections.Property<MyTable>(t => t.Volume),
        Projections.Constant(-1));

IProjection conditionalProjection = 
    Projections.Conditional(
        Restrictions.Eq(
            Projections.Property<MyTable>(t => t.MyValue), 1),
        Projections.Property<MyTable>(t => t.Volume),
        multiplicationProjection);

session.QueryOver<MyTable>()
    .SelectList(list => list
        .SelectGroup(t => t.Id)
        .Select(Projections.Sum(conditionalProjection)))
    .List<object[]>();

非常感谢Andrew - 我已经在阅读你的博客了,但我不认为这个小宝石在其中之一 :-) - mfas
@mfas:我也不这么认为 :). 很高兴我能帮到你。 - Andrew Whitaker
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - mfas
@mfas:嗯,如果我做这些更改,一切似乎都能正常工作。事实上,如果我只是将SqlFunction的结果更改为NHibernateUtil.Decimal,我就不会出错。在C#类中,Volume的类型是什么? - Andrew Whitaker
我可以看到我正在工作的项目使用的是NHibernate版本3.3.3.5000。我不知道这个版本在这方面是否有漏洞。我将尝试升级到最新版本,看看是否有所改进。但感谢您为我测试它。顺便说一下,实体上的属性也是“decimal”。 - mfas
尝试过但出现了异常:在NHibernate.dll中发生了“NHibernate.MappingException”类型的异常,但未在用户代码中处理。没有适用于NHibernate.Criterion.AggregateProjection的持久化器。 - Psddp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接