基本想法是加入两个表格,让我们在字段MYTABLE1
上称他们为MYTABLE2
和JOINID
。每个JOINID
会有很多匹配(MYTABLE1
中的一行与MYTABLE2
中的许多行相对应,为了测试,MYTABLE1
有50行),但是我们希望每个JOINID
值最多只能选择N个匹配项。我见过很多低效的解决方案,例如:
select t1.*, t2.*
from MYTABLE1 t1 inner join
(select MYTABLE2.*,
row_number() over (partition by MYTABLE2.JOINKEY order by 1) as seqnum
from MYTABLE2) t2
on t1.joinkey = t2.joinkey and seqnum <= 2;
我需要花费5分钟才能运行并返回少于100个结果,而类似
select t1.*, t2.*
from MYTABLE1 t1 inner join MYTABLE2 t2 on t1.JOINKEY = t2.JOINKEY
where rownum <= 100;
在~60毫秒内返回100个结果。
(为了确保这些结果的有效性,我选择了一个不同的测试表,并在特定的单个JOINKEY
上执行了上面的第二个查询,直到我得到的结果集少于100个结果,这意味着它确实实际上搜索所有MYTABLE2
。总查询时间是30毫秒。之后,我开始了原始查询,但这次获得了每行MYTABLE1
的50个连接,这又花了5分钟到完成)。
我怎样才能以一种非常低效的方式处理这个问题?
看起来很简单,我们需要做的就是浏览MYTABLE1
行并将JOINKEY
字段与MYTABLE2
行匹配,然后转到下一行一旦我们匹配了该行的所需数字,就MYTABLE1
。
在我的第二个示例的最坏情况下,我们应该花费30毫秒搜索每行TABLE2
的{{1}},其中有50行,总执行时间为1.5秒。
答案 0 :(得分:1)
我不会以任何方式将下面的方法称为高效并且它有点欺骗并且有一些笨拙,但它在你提供的1500ms限制之内,所以我将添加作为需要考虑的事情。
这个例子欺骗它编译TYPE
,因此它可以表示一个匿名函数。
这种方法只是使用匿名子查询因子子句函数对来自MYTABLE2
的每个JOINKEY
进行迭代探测MYTABLE1
,并在结果中累积结果。
我不知道所涉及的表的真实结构,因此这个示例假装MYTABLE2
还有一个名为CHAR
的{{1}}属性,它是{{OTHER_DATA
的目标。 1}}。
首先,设置测试表:
SELECT
然后添加测试数据。 50行到CREATE TABLE MYTABLE1 (
JOINKEY NUMBER NOT NULL
);
CREATE TABLE MYTABLE2 (
JOINKEY NUMBER NOT NULL,
OTHER_DATA CHAR(1) NOT NULL
);
CREATE INDEX MYTABLE2_I
ON MYTABLE2 (JOINKEY);
和100M行到MYTABLE1
:
MYTABLE2
然后收集统计数据......
验证表计数:
INSERT INTO MYTABLE1
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL < 51;
BEGIN
<<COMMIT_LOOP>>
FOR OUTER_POINTER IN 1..4000 LOOP
<<DATA_LOOP>>
FOR POINTER IN 1..10 LOOP
INSERT INTO MYTABLE2
SELECT
JOINKEY, OTHER_DATA
FROM
(SELECT LEVEL AS JOINKEY FROM DUAL CONNECT BY LEVEL < 51)
CROSS JOIN
(SELECT CHR(64 + LEVEL) AS OTHER_DATA FROM DUAL CONNECT BY LEVEL < 51);
END LOOP DATA_LOOP;
COMMIT;
END LOOP COMMIT_LOOP;
END;
/
然后创建一个包含所需数据的SELECT COUNT(*) FROM MYTABLE1;
50
SELECT COUNT(*) FROM MYTABLE2;
100000000
:
TYPE
然后运行一个使用匿名子查询因子块函数的查询,该函数强制每个CREATE OR REPLACE TYPE JOINKEY_OTHER_DATA IS OBJECT (JOINKEY1 NUMBER, OTHER_DATA CHAR(1));
/
CREATE OR REPLACE TYPE JOINKEY_OTHER_DATA_LIST IS TABLE OF JOINKEY_OTHER_DATA;
/
返回一个rowcount。在第一个示例中,它按JOINKEY
:
MYTABLE2
行
JOINKEY
结果:
SELECT SYSTIMESTAMP FROM DUAL;
WITH FUNCTION FETCH_N_ROWS
(P_MATCHES_LIMIT IN NUMBER)
RETURN JOINKEY_OTHER_DATA_LIST
AS
V_JOINKEY_OTHER_DATAS JOINKEY_OTHER_DATA_LIST;
BEGIN
V_JOINKEY_OTHER_DATAS := JOINKEY_OTHER_DATA_LIST();
FOR JOINKEY_POINTER IN (SELECT MYTABLE1.JOINKEY
FROM MYTABLE1)
LOOP
DECLARE
V_MYTABLE2_JOINKEYS JOINKEY_OTHER_DATA_LIST;
BEGIN
SELECT JOINKEY_OTHER_DATA(MYTABLE2.JOINKEY, MYTABLE2.OTHER_DATA)
BULK COLLECT INTO V_MYTABLE2_JOINKEYS
FROM MYTABLE2 WHERE MYTABLE2.JOINKEY = JOINKEY_POINTER.JOINKEY
FETCH FIRST P_MATCHES_LIMIT ROWS ONLY;
V_JOINKEY_OTHER_DATAS := V_JOINKEY_OTHER_DATAS MULTISET UNION ALL V_MYTABLE2_JOINKEYS;
END;
END LOOP;
RETURN V_JOINKEY_OTHER_DATAS;
END;
SELECT *
FROM TABLE (FETCH_N_ROWS(2));
/
SELECT SYSTIMESTAMP FROM DUAL;
通过更改传递给SYSTIMESTAMP
18-APR-17 03.32.10.623056000 PM -06:00
JOINKEY1 OTHER_DATA
1 A
1 B
2 A
2 B
3 A
3 B
...
49 A
49 B
50 A
50 B
100 rows selected.
SYSTIMESTAMP
18-APR-17 03.32.11.014554000 PM -06:00
的数字,可以获得具有相当一致性能的不同数据量。
FETCH_N_ROWS
返回:
...
SELECT * FROM TABLE (FETCH_N_ROWS(13));
答案 1 :(得分:0)
您无法比较这两个查询。第二个查询只是返回第一个行。第二个必须通过 all 数据返回任何内容。更合适的比较将使用:
select . . .
from (select t1.*, t2.*
from MYTABLE1 t1 inner join
MYTABLE2 t2
on t1.JOINKEY = t2.JOINKEY
order by t1.JOINKEY
) t1
where rownum <= 100;
这必须在返回任何内容之前读取所有数据,因此它更类似于使用行号。
但是,从这个查询开始:
select t1.*, t2.*
from MYTABLE1 t1 inner join
(select MYTABLE2.*,
row_number() over (partition by t2.JOINKEY order by 1) as seqnum
from MYTABLE2 t2
) t2
on t1.joinkey = t2.joinkey and seqnum <= 2;
对于此查询,您需要MYTABLE2(JOINKEY)
上的索引。如果ORDER BY
有另一个密钥,那么它也应该在查询中。