Oracle11g 在SELECT中返回类似表的结构

Oracle11g 在SELECT中返回类似表的结构,oracle11g,sqr,Oracle11g,Sqr,我正在与SQR合作编写一份报告。我不能更改数据库的结构,也不能使用PL/SQL来完成此任务 由于报告可以从远程位置运行,因此我不想从SQR中多次调用数据库。我的目标是在1SQL中返回所有内容,这些内容只包括我需要报告的记录,以便通过慢速连接增加运行时间 我现在正在使用它,但我关心的是数据库的性能 “交易记录”表具有可用于此目的的以下字段: account_num number(10) -- the account number seq_num number(10) -- not a real s

我正在与SQR合作编写一份报告。我不能更改数据库的结构,也不能使用PL/SQL来完成此任务

由于报告可以从远程位置运行,因此我不想从SQR中多次调用数据库。我的目标是在1SQL中返回所有内容,这些内容只包括我需要报告的记录,以便通过慢速连接增加运行时间

我现在正在使用它,但我关心的是数据库的性能

“交易记录”表具有可用于此目的的以下字段:

account_num number(10) -- the account number
seq_num number(10) -- not a real sequence, it is unique to account_num
check_num number(10) -- the number on the check
postdate date
主键是account_num,seq_num

示例数据如下所示:

account_num    seq_num  check_num   postdate
----------- ---------- ---------- ----------
          1         11        200 2014-07-13
          1         16        201 2014-07-14
          1         23        205 2014-07-15
          2         52        282 2014-07-13
          2         66        284 2014-07-14
          2         72        231 2014-07-15
          3         11        201 2014-07-13
          3         12        202 2014-07-14
          3         15        203 2014-07-15
account_num    seq_num  check_num   postdate
----------- ---------- ---------- ----------
          2         52        282 2014-07-13
          2         66        284 2014-07-14
          2         72        231 2014-07-15
注意:表中还有许多其他类型的事务,但是我们正在筛选事务类型的列表,这对于这个问题不是很重要,所以我忽略了这一点。所有交易(不仅仅是支票)的交易量似乎平均每月约75万笔,其中平均约有10000笔支票交易得到报告

选择标准是返回在两个日期(包括两个日期)之间发生的所有支票交易,通常是当月的第一天和当月的最后一天,如果一个账户的任何已排序支票编号之间的差值大于X,则在这种情况下,我们将使用10

使用上述样本数据,结果如下所示:

account_num    seq_num  check_num   postdate
----------- ---------- ---------- ----------
          1         11        200 2014-07-13
          1         16        201 2014-07-14
          1         23        205 2014-07-15
          2         52        282 2014-07-13
          2         66        284 2014-07-14
          2         72        231 2014-07-15
          3         11        201 2014-07-13
          3         12        202 2014-07-14
          3         15        203 2014-07-15
account_num    seq_num  check_num   postdate
----------- ---------- ---------- ----------
          2         52        282 2014-07-13
          2         66        284 2014-07-14
          2         72        231 2014-07-15
由于check_num 282和231之间的差值大于10,因此返回来自account_num 2的所有支票

我构建了以下SQL以返回上述结果:

select
  t1.*
from
  transactions t1
join (
  select
    t3.account_num,
    t3.min_postdate,
    t3.max_postdate,
    max(t3.check_diff)
  from (
    select distinct
      t4.account_num,
      lead(t4.check_num, 1, t4.check_num) over (partition by t4.account_num order by t4.check_num) - t4.check_num as check_diff,
      min(t4.postdate) over (partition by t4.account_num) min_postdate,
      max(t4.postdate) over (partition by t4.account_num) max_postdate
    from
      transactions t4
    where
      t4.postdate between trunc(sysdate,'mm') and last_day(trunc(sysdate))) t3
  group by
    t3.account_num,
    t3.min_postdate,
    t3.max_postdate
  having max(t3.check_diff) > 10) t2
    on t1.account_num = t2.account_num
    and t1.postdate between t2.min_postdate and t2.max_postdate
;
我想返回t4中所有检查的seq_num,因此我最终使用t1上的主键。我试过使用Listag,它可以将数字汇总在一起

listagg(t4.seq_num,',') within group (order by seq_num) over (partition by account_num) sqe_nums
但这就是我被困的地方。。。使用逗号分隔的字符串。我可以使用INSTR让它工作,但是它不能使用主键,而且性能很差

instr(t1.seq_num || ',', t2.seq_nbrs || ',') > 0
我试着加入其中:

join (
  select
    t2.account_num,
    regexp_substr(t2.seq_nums,'[^,]+{1}',1,level) seq_num
  from
    dual
  connect by
    level <= length(regexp_replace(t2.seq_nums,'[^,]*')) + 1) t5
  on t1.account_num = t5. accout_num 
  and t1.sqe_num = t5.seq_num
但是我应该知道,ORA-00904-t2永远不会在连接的选择中可见


有人有什么聪明的想法吗?

我会使用子查询和更多分析函数来避免连接:

select
  account_num, seq_num, check_num, postdate
from
  (
    select account_num,
      seq_num,
      check_num,
      postdate,
      max(check_gap) over (partition by account_num) as max_check_gap
    from
      (
        select account_num,
          seq_num,
          check_num,
          postdate,     
          lead(check_num) over (partition by account_num order by check_num)
            - check_num as check_gap
        from
          transactions
        where postdate between trunc(sysdate,'mm') and last_day(trunc(sysdate))
    )
  )
where
  max_check_gap > 10
order by account_num, check_num;
对于原始查询,一次误读10检查间隔规则的中间尝试,以及此版本。所有这些数据都给出了相同的结果

这并没有解决您提出的具体问题,但希望以不同的方式解决您潜在的性能问题

如果您确实想坚持使用联接,那么可以使用collect,因为联接会多次命中表,因此效率会降低。这是一种粗略的方法,表访问可能会得到改进:

select
  t1.*
from
  transactions t1
join (
  select
    t3.account_num,
    collect(t3.seq_num) as seq_nums,
    t3.min_postdate,
    t3.max_postdate,
    max(t3.check_diff)
  from (
    select distinct
      t4.account_num,
      t4.seq_num,
      lead(t4.check_num, 1, t4.check_num) over (partition by t4.account_num order by t4.check_num) - t4.check_num as check_diff,
      min(t4.postdate) over (partition by t4.account_num) min_postdate,
      max(t4.postdate) over (partition by t4.account_num) max_postdate
    from
      transactions t4
    where
      t4.postdate between trunc(sysdate,'mm') and last_day(trunc(sysdate))) t3
  group by
    t3.account_num,
    t3.min_postdate,
    t3.max_postdate
  having max(t3.check_diff) > 10) t2
    on t1.account_num = t2.account_num
    and t1.seq_num in (select * from table(t2.seq_nums))
;

.

如果你想玩,我在这里设置了这个。你可以用t1做列表,对吗?我不知道我是否错过了什么。我用t1尝试了Listag,并且能够获得seq_nums-@Joseph No-目标是使用主键从t4进入t1。因此t4需要返回列表或某种表结构中的所有seq_nums,以便可以使用它来获取t1中的确切事务,而不是像现在这样使用日期。问题中的结果就是我需要的结果。我只是想找到一种方法,使用顶级事务帐户_num,seq _numt1中的主键来获取它们。像您这样使用附加的分析函数是一种更好的方法,因为它只需在表中点击一次。太好了!不仅如此,你还回答了最初的问题。。。收集是我想要的。我得仔细阅读一下。非常感谢!