使用SQL消除相等但相反数据的最优雅方法
我有一组相对简单的数据,如下所示:使用SQL消除相等但相反数据的最优雅方法,sql,postgresql,duplicates,Sql,Postgresql,Duplicates,我有一组相对简单的数据,如下所示: invoice_id created_at amount_in_cents user_id 22348 2019-11-07 550 31773927 22349 2019-11-08 -550 31773927 22498 2019-11-10 -3400
invoice_id created_at amount_in_cents user_id
22348 2019-11-07 550 31773927
22349 2019-11-08 -550 31773927
22498 2019-11-10 -3400 2389483
22499 2019-11-10 3400 2389483
22500 2019-11-11 18000 93842938
如您所见,示例数据的前两行归属于同一个用户id,但数量与0成反比。与第3行和第4行相同。我想删除同一用户存在反向发票的所有发票,在相互之间的30天内,只留下第五行
我可以用python实现这一点,但它会大大扩展这个过程。有没有一种简单的方法可以使用SQL来实现这一点?您可以将“不存在”与相关子查询一起使用:
select t.*
from mytable t
where not exists (
select 1
from mytable t1
where
t1.user_id = t.user_id
and greatest(t1.created_at, t.created_at)
<= least(t1.created_at, t.created_at) + interval '30 days'
and t1.amount_in_cents = - t.amount_in_cents
)
not exists(不存在)条件可确保同一用户在30天内不存在其他记录,且记录量相反。我认为没有简单的解决方案。如果要删除所有匹配对,则可以枚举并删除:
select min(invoice_id), min(created_at), user_id, max(amount_in_cents) as amount_in_cents
from (select t.*,
row_number() over (partition by user_id, amount_in_cents order by created_at) as seqnum
from t
) t
group by abs(amount_in_cents), user_id, seqnum
having count(*) = 1; -- only one "matching" amount
然而,30天的限制是具有挑战性的,我认为您可能需要一个递归CTE
考虑以下数据:
1 jan 1 500
1 jan 15 500
1 feb 1 -500
1 feb 10 -500
您想要什么结果?您使用的是MySQL还是Postgres?请仅标记相关数据库。抱歉,postgres,修复了如果有多个匹配项怎么办?例如,你可以有两个550的量。这是一个很好的问题。如果有两个,我想删除一个匹配集,留下额外的550。如果在550处有两次充电,在-550处有一次充电,这将不起作用。这个问题比听起来要难得多。这对我来说很有效,如果有两个在550,一个在-550,它会删除550和-550,留下550……对吗?@AshleyO:事实上不是,Gordon Linoff正确地指出,这对这个用例不起作用-如果在30天内,两个记录550记录都将被唯一的-550记录消除。你可能也想检查另一个答案,但它不处理30天的限制。或者,用代表这些边缘情况的样本数据,对您想要实现的内容进行更详细的描述。它仍然适用于我的特定用例,但这主要是因为我没有完美地概述我的用例。在你看来,我应该去掉复选标记吗?@AshleyO:你问了这个问题,因此你完全可以接受或不接受任何答案。但实际上,我的解决方案适用于您的示例数据。你也可以考虑问一个新的问题,样本数据覆盖了边缘情况,所以从一开始你就更清楚了。