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 的更快解决方案
为了加快上述速度,您有多种选择,包括为c
和n
创建物化视图,或者在源表中预先计算n.rn - 1
和MOD(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_table
和name_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:根据变量值将一行划分到其他表中的另一行而不遣返