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
表达式中,这样我们就不必将其写出两次我们想要的结果(对于 Unprocessed
和 No-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')
目的是 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
表达式中,这样我们就不必将其写出两次我们想要的结果(对于 Unprocessed
和 No-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')