sql - SQL:如何检查付款记录中的重叠和漏洞
问题描述
我确实有一个PaymentSchedules
包含百分比信息的表格,以及这些百分比有效的日期,资源逐个资源:
| auto_numbered | res_id | date_start | date_end | org | pct |
|---------------+--------+------------+------------+-------+-----|
| 1 | A | 2018-01-01 | 2019-06-30 | One | 100 |
| 2 | A | 2019-07-01 | (NULL) | One | 60 |
| 3 | A | 2019-07-02 | 2019-12-31 | Two | 40 |
| 4 | A | 2020-01-01 | (NULL) | Two | 40 |
| 5 | B | (NULL) | (NULL) | Three | 100 |
| 6 | C | 2018-01-01 | (NULL) | One | 100 |
| 7 | C | 2019-11-01 | (NULL) | Four | 100 |
(记录#3 和#4 可以总结为一行,但故意重复,以表明date_start
和的组合有很多date_end
。)
快速读取数据:
组织“一”在2019-06-30之前全额支付资源A;然后,它继续支付 60% 的成本,但其余的 (40%) 自 2019-07-02 起由组织“Two”支付。
这应该从 2019 年 7 月 1 日开始......小的编码错误......引发了 1 天的差距。
组织“三”始终为资源 B 全额支付费用。
组织“一”从 2018 年 1 月 1 日开始全额支付资源 C 的费用……但是,从 2019 年 1 月 11 日开始,组织“四”开始支付费用……
...并且,存在编码错误:自 2019 年 11 月 1 日以来,我们确实考虑了 200% 的资源 C:记录 #6 应该已关闭(
date_end
设置为 2019 年 10 月 31 日),但还没有...
所以,当我们生成 2019 年(从 2019-01-01 到 2019-12-31)的财务报告时,我们会出现计算错误...
所以,问题是:我们如何确保我们没有重叠的资源支付,或者 - 也相反 - 在一段时间内出现“漏洞”?
如何编写SQL 查询来检查既没有少付也不多付的资源?也就是说,表中的所有资源都应在所查看的财务期间的每一天由一个或多个组织以总百分比始终等于100%的方式支付。
我不知道如何进行这样的查询。任何人都可以给出提示,让我走上正轨?
编辑——同时使用 SQL Server 和 Oracle。
编辑——我不拥有数据库,我不能添加触发器或视图。我需要能够“事后”检测事物......需要轻松发现冲突记录或“丢失”记录(以防“周期漏洞”),手动修复它们,然后重新运行财务报告。
编辑——如果我们对 2019 年进行分析,则需要以下报告:
| res_id | pct_sum | date |
|--------+---------+------------|
| A | 60 | 2019-07-01 |
| C | 200 | 2019-11-01 |
| C | 200 | 2019-11-02 |
| C | 200 | ... |
| C | 200 | ... |
| C | 200 | ... |
| C | 200 | 2019-12-30 |
| C | 200 | 2019-12-31 |
或者,当然,更好的版本——肯定无法获得?-- 一种类型的问题会出现一次,以及观察到问题的相关日期范围:
| res_id | pct_sum | date_start | date_end |
|--------+---------+------------+------------|
| A | 60 | 2019-07-01 | 2019-07-01 |
| C | 200 | 2019-11-01 | 2019-12-31 |
编辑——小提琴代码:db<>fiddle here
解决方案
这是对 Sql Server 的不完整尝试。
基本上,这个想法是使用递归 CTE 为每个 res_id 展开几个月。
然后将“可能是什么”加入现有日期范围。
但我怀疑它可以在适用于 Oracle 和 MS Sql Server 的 sql 中完成。
当然,两者都有窗口函数和 CTE。
但是对于不同的 RDMS,日期时间函数很少相同。
所以我放弃了。
也许其他人会找到更简单的解决方案。
create table PaymentSchedules ( auto_numbered int identity(1,1) primary key, res_id varchar(30), date_start date, date_end date, org varchar(30), pct decimal(3,0) ) GO
✓
insert into PaymentSchedules (res_id, org, pct, date_start, date_end) values ('A', 'One', 100, '2018-01-01', '2018-06-30') , ('A', 'One', 100, '2019-01-01', '2019-06-30') , ('A', 'One', 60, '2019-07-01', null) , ('A', 'Two', 40, '2019-07-02', '2019-12-31') , ('A', 'Two', 40, '2020-01-01', null) , ('B', 'Three', 100, null, null) , ('C', 'One', 100, '2018-01-01', null) , ('C', 'Four', 100, '2019-11-01', null) ; GO
受影响的 8 行
declare @MaxEndDate date; set @MaxEndDate = (select max(iif(date_start > date_end, date_start, isnull(date_end, date_start))) from PaymentSchedules); ;with rcte as ( select res_id , datefromparts(year(min(date_start)), month(min(date_start)), 1) as month_start , eomonth(coalesce(max(date_end), @MaxEndDate)) as month_end , 0 as lvl from PaymentSchedules group by res_id having min(date_start) is not null union all select res_id , dateadd(month, 1, month_start) , month_end , lvl + 1 from rcte where dateadd(month, 1, month_start) < month_end ) , cte_gai as ( select c.res_id, c.month_start, c.month_end , t.org, t.pct, t.auto_numbered , sum(isnull(t.pct,0)) over (partition by c.res_id, c.month_start) as res_month_pct , count(t.auto_numbered) over (partition by c.res_id, c.month_start) as cnt from rcte c left join PaymentSchedules t on t.res_id = c.res_id and c.month_start >= datefromparts(year(t.date_start), month(t.date_start), 1) and c.month_start <= coalesce(t.date_end, @MaxEndDate) ) select * from cte_gai where res_month_pct <> 100 order by res_id, month_start GO
res_id | 月开始 | 月末 | 组织 | 百分比 | 自动编号 | res_month_pct | cnt :----- | :------------ | :--------- | :--- | :--- | ------------: | :------------ | --: 一个 | 2018-07-01 | 2019-12-31 | 空| 空| 空| 0 | 0 一个 | 2018-08-01 | 2019-12-31 | 空| 空| 空| 0 | 0 一个 | 2018-09-01 | 2019-12-31 | 空| 空| 空| 0 | 0 一个 | 2018-10-01 | 2019-12-31 | 空| 空| 空| 0 | 0 一个 | 2018-11-01 | 2019-12-31 | 空| 空| 空| 0 | 0 一个 | 2018-12-01 | 2019-12-31 | 空| 空| 空| 0 | 0 C | 2019-11-01 | 2020-01-31 | 一 | 100 | 7 | 200 | 2 C | 2019-11-01 | 2020-01-31 | 四 | 100 | 8 | 200 | 2 C | 2019-12-01 | 2020-01-31 | 一 | 100 | 7 | 200 | 2 C | 2019-12-01 | 2020-01-31 | 四 | 100 | 8 | 200 | 2 C | 2020-01-01 | 2020-01-31 | 一 | 100 | 7 | 200 | 2 C | 2020-01-01 | 2020-01-31 | 四 | 100 | 8 | 200 | 2
db<>在这里摆弄
推荐阅读
- laravel - 你如何在 Laravel 项目中添加哨兵?
- node.js - 监听rabbitmq消息
- android - Android ListAdapter 在更改时全部更新
- aws-appsync - AWS AppSync:在请求映射模板中添加多个查询字符串项不起作用
- java - 如何使用线程创建循环(在循环中创建多个新线程)?
- reactjs - 如何将数据传递给 React 中的弹出模式
- c# - 如何在 FluentD 中查看日志?
- r - R; lubridate 加月给出 NA
- python - 删除文件夹但 csv 文件
- python - 如何在 azure 函数绑定中选择日期时间周数?