SqlServer中的SUM与EXIST

时间:2017-03-23 20:32:19

标签: sql-server group-by having

如果他们没有PaymentUid和NO ProcessStatus.value('/ CPI / @ ProcessItem)[1]'...关系,并且还选择'No-Matched-Payments',那么目的是返回所有'未处理'的TransactionSets 'TransactionSets,如果他们有任何PaymentUid和任何ProcessStatus.value('/ CPI / @ ProcessItem)[1]'...关系。

SUM函数看起来很笨,并且在遇到any或none时不允许SQL退出。因此,它看起来效率低下,而且阅读和处理至少相当笨拙。有没有办法用像EXIST这样的东西来写这个?

select ts.TransactionSetUid
  from TransactionSet ts
    join TransactionHeader eh on ts.TransactionSet = eh.TransactionSet
    join TransactionPayment tp on eh.TransactionHeaderUid = tp.TransactionHeaderUid
    left join ServicePayment sp on tp.TransactionPaymentUid = sp.TransactionPaymentUid
  where TransactionStatus in ('Unprocessed', 'No-Matched-Payments')
  group by ts.TransactionSet
  having (TransactionStatus = 'Unprocessed'
      and SUM( CASE WHEN sp.TransactionItem is null THEN 0 ELSE 1 END)  = 0
      and SUM( CASE WHEN tp.ProcessStatus.value('(/CPI/@ProcessItem)[1]', 'varchar(50)') IS NULL THEN 0 ELSE 1 END) = 0)
    or (ts.RuleStatus = 'No-Matched-Payments'
      and (SUM( CASE WHEN sp.TransactionItem is null THEN 0 ELSE 1 END) <> 0
        or SUM( CASE WHEN tp.ProcessStatus.value('(/CPI/@ProcessItem)[1]', 'varchar(50)') IS NULL THEN 0 ELSE 1 END) <> 0))

更新回答问题。 TransactionSet与其他表之间的关系是一对多。可能有许多TransactionPayment记录,但查询仅涉及在(/ CPI / @ processItem)[1]处具有xml节点的ProcessStatus.value。但是使用ServicePayment,任何非null的TransactionItem都可以。

据我所知,由于SUM功能,group by只在那里。目的是标记满足两个条件之一的任何TransactionSet。

The first condition is:
  the Transaction Status is 'Unprocessed'
  and
    there are no Process Status values
    and
    there are no Transaction Items.

The second condition is:
  the Transaction Status  is 'No-Matched-Payments'
  and
    there is at least one Process Status value
    or
    there is at least one Transaction Item.

因此,查询被设置为使用SUM来计算ServicePayment上的左连接出现的次数为NULL或者当TransactionPayment中的XML值不包含'/ CPI / @ processItem'时。

在我看来,查询可以改为使用EXIST或其他一些机制来短路测试条件,而不是使用SUM。 SUM的值并不重要,它只需要知道是否至少有一个或者是否有。

- 谢谢大家:我知道我不是一个数据库专家,而且我已经用七个C(C,C ++,C#,Java等)编程了很长时间,以至于我有时会忘记SQL是不是命令式语言,或者更可能的是,我只是不用声明性的术语来思考。

2 个答案:

答案 0 :(得分:0)

我觉得这样的事情可以解决问题:

select ts.TransactionSetUid
  from TransactionSet ts
  where CASE WHEN EXISTS(SELECT * FROM TransactionHeader eh
       join TransactionPayment tp on eh.TransactionHeaderUid = tp.TransactionHeaderUid
       left join ServicePayment sp on tp.TransactionPaymentUid = sp.TransactionPaymentUid
       where ts.TransactionSet = eh.TransactionSet and
       (
           sp.TransactionItem is not null or
           tp.ProcessStatus.value('(/CPI/@ProcessItem)[1]', 'varchar(50)') IS not NULL
       )
 ) THEN 1 ELSE 0 END =
        CASE TransactionStatus
              WHEN 'Unprocessed' THEN 0
              WHEN 'No-Matched-Payments' THEN 1
        END

也就是说,我已将EXISTS签入以测试任一条件并将其放入CASE表达式中,以便我们不必将其写出两次我们想要的结果(UnprocessedNo-Matched-Payments)。

我还制作了第二个CASE表达式,以返回0,1或NULL,这样如果TransactionStatus是其他内容,则无关紧要结果EXISTS产生。

我希望我在这里遵循正确的0/1,true / false和/或NULL / NOT NULL逻辑链 - 如果它不是100%,那么它有希望只是调整这些选项。我还假设我可以将TransactionSet以外的所有表格移到EXISTS - TransactionHeader可能必须留在外面TransactionStatus如果那里<em> 1}}来自。

如果这不正确,您应该在问题中添加简单表格和样本数据,以及预期结果。

答案 1 :(得分:0)

是的,这可能有用...... - 您的查询未包含select distinct,但如果这会产生重复的TransactionSetUid,请添加关键字distinct ...

select [distinct] ts.TransactionSetUid      from TransactionSet ts 
   join TransactionHeader th
       on th.TransactionSet = ts.TransactionSet
   join TransactionPayment tp 
       on tp.TransactionHeaderUid = th.TransactionHeaderUid
where not exists
      ( Select * from ServicePayment 
        Where TransactionPaymentUid = tp.TransactionPaymentUid
            and tp.ProcessStatus.value(
                  '(/CPI/@ProcessItem)[1]', 'varchar(50)') IS NULL
            and TransactionStatus = 'Unprocessed')
   Or exists
      ( Select * from ServicePayment 
        Where TransactionPaymentUid = tp.TransactionPaymentUid
            and ts.RuleStatus = 'No-Matched-Payments'
            and tp.ProcessStatus.value(
                  '(/CPI/@ProcessItem)[1]', 'varchar(50)') IS not NULL
            and ts.RuleStatus = 'No-Matched-Payments')