如何在一个oracle表中找到多个对

问题描述 投票:2回答:2

我在一个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的匹配? (该组包含多个反对票据)

我希望你能帮助我 - 谢谢你提前:)

sql database oracle
2个回答
1
投票

以下SQL代码已分别使用Oracle 12c和18c进行了测试。创意/步骤:

{1}将原始表拆分为MINUSES和PLUSES表,只包含正数,稍后节省一些函数调用。

{2}创建2个视图,找到适合特定减号的加号组合(反之亦然)。

{3}在名为ALLCOMPONENTS的表中以“逗号分隔”形式列出所有组件。

{4}表格GAPFILLERS:展开所有组件的(逗号分隔)ID,从而获得填充原始表中间隙的所有必要值。

{5} LEFT JOIN将原始表格加入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}查看:查找PLUS值的组件

-- 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}表所有组件:所有“组件”的列表

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. 

DBFIDDLE here


1
投票

qazxsw poids理想的设计,用于解决问题的强力方法(唯一的问题是,对于大数据,查询将永远挂起)。

这里有一个可能的逐步方法,第一步只考虑一个计数单,而第二步只考虑两个。

我正在显示前两个步骤的查询,你应该知道如何继续 - 很可能在循环中使用动态SQL。

第一步是琐碎的自我加入加入桌面并限制SQLGROUP。创建结果表,进一步用于限制已匹配的行。

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)s和partner_number。结果将插入临时表中。

bills

您必须继续处理3,4等行,直到分配所有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 */ ; s和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)

祝好运;)

© www.soinside.com 2019 - 2024. All rights reserved.