oracle sql中根据其他表中的计数重新启动rownumber

Posted

技术标签:

【中文标题】oracle sql中根据其他表中的计数重新启动rownumber【英文标题】:Restart of rownumber based on count in other table in oracle sql 【发布时间】:2016-01-10 11:25:38 【问题描述】:

我有两个没有公用键的表我想在没有笛卡尔连接的情况下对这两个表进行连接。 表 1 大约有 40,000 行(记录数在每天的生产中有所不同),而表 2 目前的计数为 80,000 行(记录数在每天的生产中有所不同)。

TABLE1 :- NAME_VALUES

NAME_VAL
--------
TOM
DICK
HARRY 

TABLE2 :- CUS_TABLE

CUS_ID
---------
401795480  
201134211 
137643082 
876450821 
777290153 
111035791 
579865552 

我想有一些像下面这样的输出

401795480 TOM
201134211 DICK
137643082 HARRY
876450821 DICK
777290153 HARRY
111035791 TOM
579865552 DICK

我的想法是为每个表分配行号。对于表 2,一旦达到表 1 的最大计数,我想重新启动行号,如下所示,但无法弄清楚我该如何执行

Table1
    NAME_VAL   TABLE1_RN 
    --------------------- 
    TOM          1
    DICK         2
    HARRY        3

Table2
    CUS_ID    TABLE2_RN
    --------------------
    401795480 1 
    201134211 2
    137643082 3
    876450821 1
    777290153 2
    111035791 3
    579865552 1 

现在我有了一个键,可以轻松映射以获取我需要的详细信息。

请建议是否有任何方法可以满足我的要求。

【问题讨论】:

这不是编程问题。您可以在另一个堆栈成员站点 [dba](dba.stackexchange.com) 上提出这个问题。 @davejal:为什么不呢? SQL是图灵完备的编程语言 您可能需要的是 ntile 分析函数? docs.oracle.com/cd/B19306_01/server.102/b14200/functions101.htm 或只是 dbms_random.value() 函数:docs.oracle.com/cd/B28359_01/appdev.111/b28419/…。取决于你想要达到的目标。 我是这个论坛的新手,我希望通过 SQL 而不是其他编程语言(如 PL/SQL)来执行此操作,因此在此论坛上发布。 是的@LukasEder,我们可以对此进行讨论,但最好在 dba 处询问,因为它显然与数据库 (sql) 相关。见SO的topics和dba的话题 【参考方案1】:

显示想法的规范(可能很慢)解决方案

这是一个在连接谓词上使用模运算符的相当慢的解决方案:

SELECT cus_id, name_val
FROM (
  SELECT cus_id, ROWNUM - 1 rn
  FROM cus_table
) c
JOIN (
  SELECT name_val, ROWNUM - 1 rn, MAX(ROWNUM) OVER() total
  FROM name_values
) n
ON n.rn = MOD(c.rn, n.total)
ORDER BY c.rn

以上产量

CUS_ID      NAME_VAL
--------------------
401795480   TOM
201134211   DICK
137643082   HARRY
876450821   TOM
777290153   DICK
111035791   HARRY
579865552   TOM

SQLFiddle here

使用 SQL 的更快解决方案

为了加快上述速度,您有多种选择,包括为cn 创建物化视图,或者在源表中预先计算n.rn - 1MOD(c.rn - 1, n.total) 的值,同时将这些预先计算好的表上的索引。

使用 PL/SQL 的更快解决方案

如果您被允许在您的系统中编写 PL/SQL,您显然可以为此使用 PIPELINED 函数:

CREATE TYPE rec AS OBJECT (
  cus_id NUMBER(18),
  name_val VARCHAR2(50)
);
/

CREATE TYPE tab AS TABLE OF rec;
/

CREATE OR REPLACE FUNCTION f RETURN tab PIPELINED AS
  TYPE name_vals IS TABLE OF name_values.name_val%type;
  v_name_vals name_vals;
BEGIN
  SELECT name_val
  BULK COLLECT INTO v_name_vals
  FROM name_values;

  FOR cus IN (SELECT cus_id, ROWNUM rn FROM cus_table)
  LOOP
    PIPE ROW(rec(cus.cus_id, v_name_vals(MOD(cus.rn - 1, v_name_vals.count) + 1)));
  END LOOP;
  RETURN;
END;
/

然后按如下方式使用该函数:

SELECT * FROM TABLE(f);

【讨论】:

@mikey:使用ROWNUM 而不是窗口函数的版本稍快。会更多地考虑调整... 这些表格中的任何一个都没有索引这是开发人员广告设计师设计表格的方式 我对您的系统了解不多,但物化视图肯定会加速您的查询(尽管它们会减慢cus_tablename_values 表上的DML)。另外,当您的查询运行缓慢时,我怀疑您的源表上的统计信息不正确。桌子真的经常被填满(然后又被清空)吗? 表格没有被清空,它每天都会根据发生变化的客户进行更新(增量更新)。我已经在我的生产中运行了这个 SQL 并给出了时间。 我曾尝试使用 materialized ,它每分钟提供 1000 条记录,考虑到我们大约有 80k ,这仍然很慢。有没有办法我可以使用 SQL 而不是 PL/SQl 来实现相同的目标?【参考方案2】:

使用 SQL 模型子句,不需要连接操作。

select cus_id, name_val
  from (select cus_id,
               mod(row_number() over (order by 1) - 1, (select count(*) cnt from name_values)) + 1 rn
          from cus_table)
 model
   reference nv on (select name_val, rownum rn from name_values) dimension by (rn) measures (name_val)
   main cus
   dimension by (cus_id, rn)
   measures (cast((null) as varchar2(4000)) as name_val)
   rules upsert all
 (
   name_val[any, any] = nv.name_val[cv(rn)]
 );

还有一个示例执行:

SQL> WITH
  2    name_values (name_val) AS (
  3      SELECT 'TOM'   FROM dual UNION ALL
  4      SELECT 'DICK'  FROM dual UNION ALL
  5      SELECT 'HARRY' FROM dual
  6    ),
  7    cus_table (cus_id) AS (
  8      SELECT 401795480 FROM dual UNION ALL
  9      SELECT 201134211 FROM dual UNION ALL
 10      SELECT 137643082 FROM dual UNION ALL
 11      SELECT 876450821 FROM dual UNION ALL
 12      SELECT 777290153 FROM dual UNION ALL
 13      SELECT 111035791 FROM dual UNION ALL
 14      SELECT 579865552 FROM dual
 15    )
 16  select cus_id, name_val
 17    from (select cus_id,
 18                 mod(row_number() over (order by 1) - 1, (select count(*) cnt from name_values)) + 1 rn
 19            from cus_table)
 20   model
 21     reference nv on (select name_val, rownum rn from name_values) dimension by (rn) measures (name_val)
 22     main cus
 23     dimension by (cus_id, rn)
 24     measures (cast((null) as varchar2(4000)) as name_val)
 25     rules upsert all
 26   (
 27     name_val[any, any] = nv.name_val[cv(rn)]
 28   );
    CUS_ID NAME_VAL
---------- --------------------
 401795480 TOM
 876450821 TOM
 579865552 TOM
 201134211 DICK
 777290153 DICK
 137643082 HARRY
 111035791 HARRY
7 rows selected

【讨论】:

以上是关于oracle sql中根据其他表中的计数重新启动rownumber的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL:根据变量值将一行划分到其他表中的另一行而不遣返

根据同一张表中的其他数据验证插入的数据(Oracle)

位于其他表中的值之间的计数的 SQL 查询

SQL:根据另一个表的计数结果更新一个表中的列

根据参数 -Oracle 重新启动 sum(over) 函数

使用 PL/SQL 根据 Oracle SQL 中的父项更新子计数器