수백만 개의 행이 있는 테이블을 사용하여 Oracle에서 임의 행을 더 빠르게 선택할 수 있는 방법은 무엇입니까?
수백만 개의 행이 있는 테이블을 사용하여 Oracle에서 랜덤 행을 더 빠르게 선택할 수 있는 방법이 있습니까?sample(x)와 dbms_random.value를 사용하려고 했는데 실행하는 데 시간이 오래 걸립니다.
적절한 값 사용sample(x)
그게 당신이 할 수 있는 가장 빠른 방법입니다.블럭 내에서는 블럭 랜덤 및 행 랜덤이므로 랜덤 행을 하나만 사용하려면 다음을 수행합니다.
select dbms_rowid.rowid_relative_fno(rowid) as fileno,
dbms_rowid.rowid_block_number(rowid) as blockno,
dbms_rowid.rowid_row_number(rowid) as offset
from (select rowid from [my_big_table] sample (.01))
where rownum = 1
하위 파티션 테이블을 사용하고 있으며, 여러 행을 잡아도 상당히 많은 랜덤성을 얻을 수 있습니다.
select dbms_rowid.rowid_relative_fno(rowid) as fileno,
dbms_rowid.rowid_block_number(rowid) as blockno,
dbms_rowid.rowid_row_number(rowid) as offset
from (select rowid from [my_big_table] sample (.01))
where rownum <= 5
FILENO BLOCKNO OFFSET
---------- ---------- ----------
152 2454936 11
152 2463140 32
152 2335208 2
152 2429207 23
152 2746125 28
당신이 아마도 당신의 음악을 조정해야 할 것 같습니다.SAMPLE
가져올 항목에 적합한 샘플 크기를 사용하는 절입니다.
먼저 아담의 대답부터 시작하지만, 만약에.SAMPLE
ROWNUM 최적화를 사용하더라도 블록 샘플을 사용할 수 있습니다.
....FROM [table] SAMPLE BLOCK (0.01)
이렇게 하면 각 행 대신 블럭 수준에서 샘플링이 적용됩니다.즉, 테이블에서 많은 양의 데이터를 건너뛸 수 있으므로 표본 백분율이 매우 거칠어집니다.백분율이 낮은 SAMPLE BLOCK이 행을 0개 반환하는 것은 드문 일이 아닙니다.
다음은 AskTom에 대한 동일한 질문입니다.
http://asktom.oracle.com/pls/apex/f?p=100:11:0 ::::P11_QUESTION_ID:6075151195522
테이블 크기를 알고 있는 경우 위에서 설명한 대로 샘플 블록을 사용합니다.그렇지 않은 경우 아래 루틴을 수정하여 원하는 행 수를 얻을 수 있습니다.
복사처: http://asktom.oracle.com/pls/apex/f?p=100:11:0 ::::P11_QUESTION_ID:6075151195522#56174726207861
create or replace function get_random_rowid
( table_name varchar2
) return urowid
as
sql_v varchar2(100);
urowid_t dbms_sql.urowid_table;
cursor_v integer;
status_v integer;
rows_v integer;
begin
for exp_v in -6..2 loop
exit when (urowid_t.count > 0);
if (exp_v < 2) then
sql_v := 'select rowid from ' || table_name
|| ' sample block (' || power(10, exp_v) || ')';
else
sql_v := 'select rowid from ' || table_name;
end if;
cursor_v := dbms_sql.open_cursor;
dbms_sql.parse(cursor_v, sql_v, dbms_sql.native);
dbms_sql.define_array(cursor_v, 1, urowid_t, 100, 0);
status_v := dbms_sql.execute(cursor_v);
loop
rows_v := dbms_sql.fetch_rows(cursor_v);
dbms_sql.column_value(cursor_v, 1, urowid_t);
exit when rows_v != 100;
end loop;
dbms_sql.close_cursor(cursor_v);
end loop;
if (urowid_t.count > 0) then
return urowid_t(trunc(dbms_random.value(0, urowid_t.count)));
end if;
return null;
exception when others then
if (dbms_sql.is_open(cursor_v)) then
dbms_sql.close_cursor(cursor_v);
end if;
raise;
end;
/
show errors
이 질문에 대한 아래 솔루션은 정확한 답이 아니지만 많은 시나리오에서 행을 선택하고 특정 용도로 사용하려고 시도한 다음 다시 선택하지 않도록 "사용됨" 또는 "완료"로 상태를 업데이트합니다.
솔루션:
아래 쿼리는 유용하지만 테이블이 크면 이 쿼리로 인해 성능 문제가 발생할 수 있습니다.
SELECT * FROM ( SELECT * FROM table Order by dbms_dbms.value) 여기서 rownum = 1
따라서 아래와 같이 행을 설정하면 성능 문제를 해결할 수 있습니다.로우넘을 증가시키면 가능성을 줄일 수 있습니다.그러나 이 경우에는 항상 동일한 1000개 행의 행을 얻을 수 있습니다.1000에서 행을 얻고 "USED"로 상태를 업데이트하면 "ACTIVE"로 쿼리할 때마다 거의 다른 행이 표시됩니다.
SELECT * FROM
( SELECT * FROM table
where rownum < 1000
and status = 'ACTIVE'
ORDER BY dbms_random.value )
WHERE rownum = 1
선택한 후 행 상태를 업데이트합니다. 업데이트할 수 없는 경우 다른 트랜잭션에서 이미 사용하고 있습니다.그러면 새 행을 가져와서 상태를 업데이트해야 합니다.그런데 같은 행을 두 개의 다른 거래 가능성으로 얻는 것은 rownum이 1000이기 때문에 0.001입니다.
누군가가 샘플(x)이 당신이 할 수 있는 가장 빠른 방법이라고 말했습니다.하지만 저는 이 방법이 샘플(x) 방법보다 조금 더 빨리 작동합니다.표의 크기에 관계없이 두 번째 값(나의 경우 0.2)의 일부를 사용해야 합니다.힌트(--+ 선행(e) use_nl(et) rowid(t))를 사용하는 것이 도움이 될 수 있습니다.
SELECT *
FROM My_User.My_Table
WHERE ROWID = (SELECT MAX(t.ROWID) KEEP(DENSE_RANK FIRST ORDER BY dbms_random.value)
FROM (SELECT o.Data_Object_Id,
e.Relative_Fno,
e.Block_Id + TRUNC(Dbms_Random.Value(0, e.Blocks)) AS Block_Id
FROM Dba_Extents e
JOIN Dba_Objects o ON o.Owner = e.Owner AND o.Object_Type = e.Segment_Type AND o.Object_Name = e.Segment_Name
WHERE e.Segment_Name = 'MY_TABLE'
AND(e.Segment_Type, e.Owner, e.Extent_Id) =
(SELECT MAX(e.Segment_Type) AS Segment_Type,
MAX(e.Owner) AS Owner,
MAX(e.Extent_Id) KEEP(DENSE_RANK FIRST ORDER BY Dbms_Random.Value) AS Extent_Id
FROM Dba_Extents e
WHERE e.Segment_Name = 'MY_TABLE'
AND e.Owner = 'MY_USER'
AND e.Segment_Type = 'TABLE')) e
JOIN My_User.My_Table t
ON t.Rowid BETWEEN Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 0)
AND Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 32767))
반환된 행이 없을 때 재시도하는 버전:
WITH gen AS ((SELECT --+ inline leading(e) use_nl(e t) rowid(t)
MAX(t.ROWID) KEEP(DENSE_RANK FIRST ORDER BY dbms_random.value) Row_Id
FROM (SELECT o.Data_Object_Id,
e.Relative_Fno,
e.Block_Id + TRUNC(Dbms_Random.Value(0, e.Blocks)) AS Block_Id
FROM Dba_Extents e
JOIN Dba_Objects o ON o.Owner = e.Owner AND o.Object_Type = e.Segment_Type AND o.Object_Name = e.Segment_Name
WHERE e.Segment_Name = 'MY_TABLE'
AND(e.Segment_Type, e.Owner, e.Extent_Id) =
(SELECT MAX(e.Segment_Type) AS Segment_Type,
MAX(e.Owner) AS Owner,
MAX(e.Extent_Id) KEEP(DENSE_RANK FIRST ORDER BY Dbms_Random.Value) AS Extent_Id
FROM Dba_Extents e
WHERE e.Segment_Name = 'MY_TABLE'
AND e.Owner = 'MY_USER'
AND e.Segment_Type = 'TABLE')) e
JOIN MY_USER.MY_TABLE t ON t.ROWID BETWEEN Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 0)
AND Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 32767))),
Retries(Cnt, Row_Id) AS (SELECT 1, gen.Row_Id
FROM Dual
LEFT JOIN gen ON 1=1
UNION ALL
SELECT Cnt + 1, gen.Row_Id
FROM Retries
LEFT JOIN gen ON 1=1
WHERE Retries.Row_Id IS NULL AND Retries.Cnt < 10)
SELECT *
FROM MY_USER.MY_TABLE
WHERE ROWID = (SELECT Row_Id
FROM Retries
WHERE Row_Id IS NOT NULL)
의사 임의 행을 사용할 수 있습니까?
select * from (
select * from ... where... order by ora_hash(rowid)
) where rownum<100
언급URL : https://stackoverflow.com/questions/3150550/how-to-make-selecting-random-rows-in-oracle-faster-with-table-with-millions-of-r
'programing' 카테고리의 다른 글
Oracle: 여러 열을 동시에 업데이트 (0) | 2023.07.20 |
---|---|
큰따옴표가 있는 문자열을 표에 삽입 (0) | 2023.07.20 |
'http:/.127.0.0.1:%HTPORT%/apex/f?p=4950'을(를) 찾을 수 없습니다.이름을 올바르게 입력했는지 확인한 다음 다시 시도하십시오. (0) | 2023.07.15 |
보안이 활성화된 상태에서 Spring Boot 1.4 테스트를 수행하시겠습니까? (0) | 2023.07.15 |
Git에서 하위 디렉터리를 체크아웃하시겠습니까? (0) | 2023.07.15 |