我有两个名为 ParentTransaction 和 ChildTransaction 的交易表,其中 ParentTransaction 的TransactionId
将对 {Trantaction TransactionId
。
现在我想得到那些payamount
未完成的ParentTransaction的TransactionId。
从下面输出我想要事务ID 3的记录,因为只有1000 transactionid
3而不是5000。
我有一张这样的表:
Transactionid(p.k) PayAmount
1 1000
2 3000
3 5000
4 6000
ChildTransaction
Id TransactionId(F.k) DepositAmount
1 1 600
2 1 400
3 2 1000
4 2 1000
5 2 1000
6 3 2000
这是我的疑问:
var data = (from tmp in context.ParentTransaction
join tmp1 in context.ChildTransaction on tmp.Transactionid equals
tmp1.Transactionid where tmp.PayAmount !=tmp1.DepositAmount
select tmp);
但是在这里我得到了交易ID 1和2,尽管他们的交易已经完成了两个部分,即交易ID 1的600和400.
答案 0 :(得分:13)
查询语言的一般概念是表达期望的结果,而不是如何获得它。
将它应用于您的场景会导致像这样的简单查询
var query = context.ParentTransaction
.Where(t => t.PayAmount != context.ChildTransaction
.Where(ct => ct.TransactionId == t.TransactionId)
.Sum(ct => ct.DepositAmount));
如果您使用的是 EF 和正确的模型导航属性,那就更简单了
var query = context.ParentTransaction
.Where(t => t.PayAmount != t.ChildTransactions.Sum(ct => ct.DepositAmount));
有人可能会说,与让@Vadim Martynov的回答相比,上述内容效率低下。好吧,可能是的,可能不是。 Vadim试图强制执行特定的执行计划,我可以理解 - 我们必须在实际遇到查询性能问题时执行此类操作。但它不自然,只有在遇到性能问题时才应该是最后的手段。在大多数情况下,查询提供程序和SQL查询优化器将为我们完成(并且正在执行)该工作,因此我们不需要考虑是否需要使用join
vs {{1}等等。
答案 1 :(得分:9)
我不确定!=
是否是最佳价值。以下是>
检查和分组的解决方案:
var expectedValue =
context.ChildTransaction
.GroupBy(t => t.TransactionId, (key, group) => new { TransactionId = key, Deposit = group.Sum(e => e.Deposit) })
.Join(context.ParentTransaction, grouped => grouped.TransactionId, transaction => transaction.TransactionId, (group, transaction) => new { Transaction = transaction, group.Deposit })
.Where(result => result.Transaction.PayAmount > result.Deposit)
.Select(result => result.Transaction);
此查询可以按照下一个要求的声明方式阅读:
TransactionId
收集子事务,并为每个组检索anonymous type个对象,其中包含字段TransactionId =分组键(== TransactionId)和存款,它是行的存款总和使用相同的TransactionId。PaerntTransaction
字段从第1部分设置到表TransactionId
。对于每个联接对,检索一个anonymous type对象,其中包含来自ParentTransactions
表的字段Transaction == transaction和来自第1部分的存款,这是来自{TransactionId
的存款之和。 {1}}表。ChildTransactions
个对象。这是SQL优化的方案,因为连接,过滤和分组会阻止nested queries在其他情况下可以添加到实际执行计划中并使性能更差。
<强>更新强>
要解决没有存款的交易问题,您可以使用LEFT JOIN:
ParentTransaction
与LINQ方法链相同的查询:
var expectedValue = from parent in context.ParentTransaction
join child in context.ChildTransaction on parent.TransactionId equals child.TransactionId into gj
from subset in gj.DefaultIfEmpty()
let joined = new { Transaction = parent, Deposit = subset != null ? subset.Deposit : 0 }
group joined by joined.Transaction
into grouped
let g = new { Transaction = grouped.Key, Deposit = grouped.Sum(e => e.Deposit) }
where g.Transaction.PayAmount > g.Deposit
select g.Transaction;
现在您检索所有父事务并将其与子事务连接,但如果没有子事务,则使用var expectedValue =
context.ParentTransaction
.GroupJoin(context.ChildTransaction, parent => parent.TransactionId, child => child.TransactionId, (parent, gj) => new { parent, gj })
.SelectMany(@t => @t.gj.DefaultIfEmpty(), (@t, subset) => new { @t, subset })
.Select(@t => new { @t, joined = new { Transaction = @t.@t.parent, Deposit = @t.subset != null ? @t.subset.Deposit : 0 } })
.GroupBy(@t => @t.joined.Transaction, @t => @t.joined)
.Select(grouped => new { grouped, g = new { Transaction = grouped.Key, Deposit = grouped.Sum(e => e.Deposit) } })
.Where(@t => @t.g.Transaction.PayAmount > @t.g.Deposit)
.Select(@t => @t.g.Transaction);
并按Deposit == 0
以类似方式对连接的实体进行分组。
答案 2 :(得分:4)
问题出在这个声明上:
where tmp.PayAmount != tmp1.DepositAmount //the culprit
由于tmp1
被定义为单个子事务,因此该语句会导致错误的值等于:
<强>展示台强>:
1000 != 600 //(result: true -> selected) comparing parent 1 and child 1
1000 != 400 //(result: true -> selected) comparing parent 1 and child 2
3000 != 1000 //(result: true -> selected) comparing parent 2 and child 3
3000 != 1000 //(result: true -> selected) comparing parent 2 and child 4
3000 != 1000 //(result: true -> selected) comparing parent 2 and child 5
5000 != 2000 //(result: true -> selected) comparing parent 2 and child 5
//However, you do not want it to behave like this actually
但你想拥有的是:
<强>展示台强>:
1000 != (600 + 400) //(result: false -> not selected) comparing parent 1 and child 1 & 2, based on the TransactionId
3000 != (1000 + 1000 + 1000) //(result: false -> not selected) comparing parent 2 and child 3, 4, & 5, based on the TransactionId
5000 != (2000) //(result: true -> selected) comparing parent 3 and child 6, based on the TransactionId
6000 != nothing paid //(result: true -> selected) comparing parent 3 with the whole childTransaction and found there isn't any payment made
因此,您应该将tmp1
作为集合的子项而不是单个子项。
更改您的代码:
var data = (from tmp in context.ParentTransaction
join tmp1 in context.ChildTransaction.GroupBy(x => x.TransactionId) //group this by transaction id
on tmp.TransactionId equals tmp1.Key //use the key
where tmp.PayAmount > tmp1.Sum(x => x.DepositAmount) //get the sum of the deposited amount
select tmp)
.Union( //added after edit
(from tmp in context.ParentTransaction
where !context.ChildTransaction.Select(x => x.TransactionId).Contains(tmp.TransactionId)
select tmp)
);
<强>说明强>
这一行:
join tmp1 in context.ChildTransaction.GroupBy(x => x.TransactionId) //group this by transaction id
利用GroupBy
中的Linq
,此行使tmp1
成为群组的孩子,而不是单身子女,理所当然,基于其外键,即TransactionId
。
然后这一行:
on tmp.TransactionId equals tmp1.Key //use the key
我们只是将tmp.TransactionId
与孩子的群组密钥tmp1.Key
然后是下一行:
where tmp.PayAmount > tmp1.Sum(x => x.DepositAmount) //get the sum of the deposited amount
获取孩子的DepositAmount
而不是单个孩子的DepositAmount
的总和值,该值小于父亲中的PayAmount
,然后
select tmp
选择满足上述所有条件的所有父交易。这样,我们完成了一半。
下一步是考虑在父级而非子级(ren)中发生的事务。这也被认为是无偿的。
我们可以使用query
query
的结果与第二个Union
的结果相结合
.Union( //added after edit
(from tmp in context.ParentTransaction
where !context.ChildTransaction.Select(x => x.TransactionId).Contains(tmp.TransactionId)
select tmp)
);
这会选择父交易中存在的任何内容,但在子交易中根本不存在(因此被视为未支付)。
您将获得正确的data
,其中包含未完全付款的ParentTransaction
行,这两行都适用于子级中存在TransactionId
的父级交易。
对于付费交易,只需将查询从>
更改为<=
:
var datapaid = (from tmp in context.ParentTransaction
join tmp1 in context.ChildTransaction.GroupBy(y => y.TransactionId)
on tmp.TransactionId equals tmp1.Key
where tmp.PayAmount <= tmp1.Sum(x => x.DepositAmount)
select tmp);
我们可以进一步简化上述查询:
var grp = context.ChildTransaction.GroupBy(y => y.TransactionId);
var data = (from tmp in context.ParentTransaction
join tmp1 in grp //group this by transaction id
on tmp.TransactionId equals tmp1.Key //use the key
where tmp.PayAmount > tmp1.Sum(x => x.DepositAmount)
select tmp)
.Union((
from tmp in context.ParentTransaction
where !context.ChildTransaction.Select(x => x.TransactionId).Contains(tmp.TransactionId)
select tmp));
var datapaid = (from tmp in context.ParentTransaction
join tmp1 in grp
on tmp.TransactionId equals tmp1.Key
where tmp.PayAmount <= tmp1.Sum(x => x.DepositAmount)
select tmp);
答案 3 :(得分:2)
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.2.Final</version>
</dependency>
答案 4 :(得分:2)
您只能控制一个子事务。您必须使用Sum()
操作,并且需要使用>
代替!=
请尝试此操作。
var data = (from tmp in context.ParentTransaction
join tmp1 in context.ChildTransaction on tmp.Transactionid equals into tmp1List
tmp1.Transactionid where tmp.PayAmount > tmp1List.Sum(l => l.DepositAmount)
select tmp);