SQL Server 中的 SUM 与 EXIST

SUM vs EXIST in SqlServer

目的是 return 所有 'Unprocessed' TransactionSets 如果它们没有 PaymentUid 并且没有 ProcessStatus.value('/CPI/@ProcessItem)[1]'... 关系,如果它们有任何 PaymentUid 和任何 ProcessStatus.value('/CPI/@ProcessItem)[1]'... 关系,也可以选择 'No-Matched-Payments' TransactionSets。

having 中的 SUM 函数看起来很笨重,不允许 SQL 在遇到任何或 none 时退出。所以它看起来效率低下,至少阅读和处理起来非常笨拙。有没有办法用类似 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 记录,但查询只涉及 ProcessStatus.value,它在 (/CPI/@processItem)[1] 处有一个 xml 节点。但是对于 ServicePayment,任何非空的 TransactionItem 都可以。

据我了解,group by 只是因为 SUM 函数才在那里。目的是标记满足两个条件之一的任何 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的值其实并不重要,只需要知道是否至少有一个或者是否有none.

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

我认为像这样的东西应该可以解决问题:

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 表达式设计为 return 0、1 或 NULL,这样如果 TransactionStatus 是其他东西,它就不会不管 EXISTS 产生什么结果。

我希望我遵循了正确的 0/1、true/false、and/or、NULL/NOT NULL 逻辑链 - 如果不是 100%,希望只是调整那些选项。我还假设我可以将除 TransactionSet 之外的所有表格转移到 EXISTS - 如果 TransactionStatus 来自那里, TransactionHeader 可能必须留在外面.

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

是的,这可能有效... -- 您的查询不包含 select 个不同的,但如果这会产生重复的 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')