在一个Oracle-Table中查找匹配项时遇到问题,希望您能对我有所帮助。
我有一个帐单表,其中包含如下预订数据:
ID GROUP Bill-Number Value Partner-Number
1 1 111 10,90
2 1 751 40,28
3 1 438 125,60
4 1 659 -10,90 987
5 1 387 -165,88 755
6 1 774 -100,10
7 1 664 -80,12
8 1 259 180,22 999
9 2 774 -200,10
10 2 664 -80,12
11 2 259 280,22 777
如您所见,我们有一些包含费用的账单。 一段时间后,出现了对以前费用进行汇总的反发票。账单和相关的反发票的总和将创建值为0。
示例:value of (id 2 + id 3 = id 5*-1)
或数字:40,28 + 125,60 + (-165,88) = 0
还价单中包含“合作伙伴编号”。我需要将此信息添加到关联的帐单中。
解决方案应如下所示:
ID GROUP Bill-Number Value Partner-Number
1 1 111 10,90 987
2 1 751 40,28 755
3 1 438 125,60 755
4 1 659 -10,90 987
5 1 387 -165,88 755
6 1 774 -100,10 999
7 1 664 -80,12 999
8 1 259 180,22 999
9 2 774 -200,10 777
10 2 664 -80,12 777
11 2 259 280,22 777
我只需要在一组内匹配账单。 (ID是我的主键) 只要该组包含一个与票据具有1:1关系的反票据,对我来说就是可行的。
但是我如何找到像第1组那样的比例为1:N的比赛? (该组包含多个反制)
希望您能帮到我-谢谢您:)
答案 0 :(得分:1)
SQL
是解决
强力问题的理想选择(唯一的问题是,对于大数据,查询将永远挂)
这里是一种可能的分步方法,在第一步中只考虑一个反付帐,在第二步中只考虑两个反付帐,等等。
我正在显示前两个步骤的查询,您应该了解如何进行操作-最有可能在循环中使用动态SQL。
第一步是简单的自联接,联接表并约束GROUP
和value
。将创建一个结果表,该表将进一步用于限制已经匹配的行。
create table tab_match as
-- 1 row match
select b.ID, b.GROUP_ID, b.BILL_NUMBER, b.VALUE, a.partner_number from tab a
join tab b
on a.group_id = b.group_id and /* same group */
-1 * a.value = b.value /* oposite value */
where a.partner_number is not NULL /* consider group row only */
在第二步中,您重复相同的操作,仅添加一个联接(我们调查两个子帐单),并对总值-1 * a.value = (b.value + c.value)
附加约束
此外,我们还会取消所有已分配的partner_number
和bills
。结果插入到临时表中。
insert into tab_match (ID, GROUP_ID, BILL_NUMBER, VALUE, PARTNER_NUMBER)
select b.ID, b.GROUP_ID, b.BILL_NUMBER, b.VALUE, a.partner_number partner_number_match from tab a
join tab b
on a.group_id = b.group_id and /* same group */
sign(a.value) * sign(b.value) < 0 and /* values must have oposite signs */
abs(a.value) > abs(b.value) /* the partial value is lower than the sum */
join tab c /* join to 2nd table */
on a.group_id = c.group_id and
sign(a.value) * sign(c.value) < 0 and
abs(a.value) > abs(c.value) and
-1 * a.value = (b.value + c.value)
where a.partner_number is not NULL and /* consider open group row only */
a.partner_number not in (select partner_number from tab_match) and
a.id not in (select id from tab_match) /* ignore matched rows */
;
您必须继续处理3,4等行,直到分配了所有partner_number
和bills
。
添加下一个联接
join tab d
on a.group_id = d.group_id and
sign(a.value) * sign(d.value) < 0 and
abs(a.value) > abs(d.value)
并在每个步骤中调整总谓词
-1 * a.value = (b.value + c.value + d.value)
祝你好运;)
答案 1 :(得分:1)
以下SQL代码已分别与Oracle 12c和18c进行了测试。想法/步骤:
{1}将原始表拆分为MINUSES和PLUSES表,其中仅包含正数,从而为我们节省了一些函数调用。
{2}创建2个视图,这些视图将找到适合特定负号的正负号的组合(反之亦然)。
{3}在“ ALLCOMPONENTS”表中以“逗号分隔”的形式列出所有组件。
{4}表GAPFILLERS:扩展所有组件的ID(以逗号分隔),从而获得所有必要的值以填补原始表中的空白。
{5}将原始表左移到GAPFILLERS。
原始表格/数据
create table bills ( id primary key, bgroup, bnumber, bvalue, partner )
as
select 1, 1, 111, 10.90, null from dual union all
select 2, 1, 751, 40.28, null from dual union all
select 3, 1, 438, 125.60, null from dual union all
select 4, 1, 659, -10.90, 987 from dual union all
select 5, 1, 387, -165.88, 755 from dual union all
select 6, 1, 774, -100.10, null from dual union all
select 7, 1, 664, -80.12, null from dual union all
select 8, 1, 259, 180.22, 999 from dual union all
select 9, 2, 774, -200.10, null from dual union all
select 10, 2, 664, -80.12, null from dual union all
select 11, 2, 259, 280.22, 777 from dual ;
{1}将表拆分为PLUS和MINUS表
-- MINUSes
create table minuses as
select id
, bgroup as mgroup
, bnumber as mnumber
, bvalue * -1 as mvalue
, partner as mpartner
from bills where bvalue < 0 ;
-- PLUSes
create table pluses as
select id
, bgroup as pgroup
, bnumber as pnumber
, bvalue as pvalue
, partner as ppartner
from bills where bvalue >= 0 ;
{2}视图:查找PLUSvalues的组成部分
-- used here: "recursive subquery factoring"
-- and LATERAL join (needs Oracle 12c or later)
create or replace view splitpluses
as
with recursiveclause ( nextid, mgroup, tvalue, componentid )
as (
select -- anchor member
id as nextid
, mgroup as mgroup
, mvalue as tvalue -- total value
, to_char( id ) as componentid
from minuses
union all
select -- recursive member
M.id
, R.mgroup
, R.tvalue + M.mvalue
, R.componentid || ',' || to_char( M.id )
from recursiveclause R
join minuses M
on M.id > R.nextid and M.mgroup = R.mgroup -- only look at values in the same group
)
--
select
mgroup
, tvalue as plusvalue
, componentid as minusids
, ppartner
from
recursiveclause R
, lateral ( select ppartner from pluses P where R.tvalue = P.pvalue ) -- fetch the partner id
where
tvalue in ( select pvalue from pluses where ppartner is not null ) -- get all relevant pvalues that must be broken down into components
and ppartner is not null -- do this for all pluses that have a partner id
;
{2b}视图:查找MINUSvalues的分量
create or replace view splitminuses
as
with recursiveclause ( nextid, pgroup, tvalue, componentid )
as (
select -- anchor member
id as nextid
, pgroup as pgroup
, pvalue as tvalue -- total value
, to_char( id ) as componentid
from pluses
union all
select -- recursive member
P.id
, R.pgroup
, R.tvalue + P.pvalue
, R.componentid || ',' || to_char( P.id )
from recursiveclause R
join pluses P
on P.id > R.nextid and P.pgroup = R.pgroup
)
--
select
pgroup
, tvalue as minusvalue
, componentid as plusids
, mpartner
from
recursiveclause R
, lateral ( select mpartner from minuses M where R.tvalue = M.mvalue )
where
tvalue in ( select mvalue from minuses where mpartner is not null )
and mpartner is not null
;
视图为我们提供了以下结果集:
SQL> select * from splitpluses;
MGROUP PLUSVALUE MINUSIDS PPARTNER
1 180.22 6,7 999
2 280.22 9,10 777
SQL> select * from splitminuses ;
PGROUP MINUSVALUE PLUSIDS MPARTNER
1 10.9 1 987
1 165.88 2,3 755
{3}表ALLCOMPONENTS:所有“组件”的列表
create table allcomponents ( type_, group_, value_, cids_, partner_ )
as
select 'components of PLUS' as type_, M.* from splitminuses M
union all
select 'components of MINUS', P.* from splitpluses P
;
SQL> select * from allcomponents ;
TYPE_ GROUP_ VALUE_ CIDS_ PARTNER_
components of PLUS 1 10.9 1 987
components of PLUS 1 165.88 2,3 755
components of MINUS 1 180.22 6,7 999
components of MINUS 2 280.22 9,10 777
{4}表GAPFILLERS:派生自ALLCOMPONENTS,包含我们需要填充原始表中“间隙”的所有值。
-- One row for each CSV (comma-separated value) of ALLCOMPONENTS
create table gapfillers
as
select unique type_, group_, value_
, trim( regexp_substr( cids_, '[^,]+', 1, level ) ) cids_
, partner_
from (
select type_, group_, value_, cids_, partner_
from allcomponents
) AC
connect by instr( cids_, ',', 1, level - 1 ) > 0
order by group_, partner_ ;
SQL> select * from gapfillers ;
TYPE_ GROUP_ VALUE_ CIDS_ PARTNER_
components of PLUS 1 165.88 2 755
components of PLUS 1 165.88 3 755
components of PLUS 1 10.9 1 987
components of MINUS 1 180.22 6 999
components of MINUS 1 180.22 7 999
components of MINUS 2 280.22 10 777
components of MINUS 2 280.22 9 777
7 rows selected.
{5}最终的LEFT JOIN
select
B.id, bgroup, bnumber, bvalue
, case
when B.partner is null then G.partner_
else B.partner
end as partner
from bills B
left join gapfillers G on B.id = G.cids_
order by 1 ;
-- result
ID BGROUP BNUMBER BVALUE PARTNER
1 1 111 10.9 987
2 1 751 40.28 755
3 1 438 125.6 755
4 1 659 -10.9 987
5 1 387 -165.88 755
6 1 774 -100.1 999
7 1 664 -80.12 999
8 1 259 180.22 999
9 2 774 -200.1 777
10 2 664 -80.12 777
11 2 259 280.22 777
11 rows selected.