Oracle 데이터베이스 위경도 좌표 쿼리 최적화 및 결과 오류 원인 분석, SQL에서 WKT 초장 텍스트 문자열 처리

1. Oracle 기하학적 공간 데이터 객체와 기타 데이터베이스의 차이점

MySQL, PostgreSQL 등의 데이터베이스에 비해 오라클 데이터베이스의 지리적 기하학적 공간은 배우고 사용하기가 더 어렵습니다.. 요약한 이유 중 하나는 Oracle Spatial 문서에서 일반적이지 않은 기하학적 객체 구성 및 쿼리 방법을 너무 많이 설명하고 있다는 것입니다. 다른 데이터베이스. Oracle Spatial 문서를 별도로 깊이 연구해야 하며 지식이 심각하게 단편화되어 있습니다. 동일한 기능을 구현하려면 호출할 수 있는 유사한 기능을 가진 N개의 함수가 있을 수 있습니다(회향이라는 단어를 작성하는 N가지 방법) 회향콩) 학습 비용이 너무 높고, 충분하지 않습니다. 주의를 기울이면 함정에 빠지기 쉽습니다.

WKT(Well Known Text)는 기하학적 공간 개체에 대한 보편적인 텍스트 표현 방법입니다. 똑같이 보편적인 GeoJSON 텍스트에 비해 WKT는 더 간단하고 이해하기 쉽고 생성하기 쉽습니다. 중요한 것은 지리적 기하학적 공간을 지원하는 거의 모든 데이터베이스가 WKT.text를 지원합니다.

  • MySQL, 기하학적 개체를 구성하는 데 PostgreSQL사용할 수 있습니다.ST_GeomFromText('wkt',SRID)
  • SQLServergeometry::STGeomFromText('wkt',SRID)기하학적 객체를 구성하는 데 사용할 수 있습니다.
  • OracleSDO_GEOMETRY('wkt',SRID)기하학적 객체를 구성하는 데 사용할 수 있지만 여기에는 함정이 있습니다.

Oracle에서도 SQL 문에서 손으로 쓴 단일 텍스트 문자열의 길이가 제한되어 있는데, 테스트 결과 SQLPlus에서는 약 3000 정도, SQL Developer에서는 약 32767 정도인 것으로 나타났습니다. SQL이 실행되지 않고 구문 분석이 직접 보고됩니다 .다른 데이터베이스에서는 볼 수 없는 오류입니다. 지리적 좌표 경계 그래픽의 복잡한 WKT에는 수백 KB 또는 심지어 MB에 달하는 매우 긴 텍스트가 포함될 수 있으며 이는 단순히 Oracle에서 SQL 문을 사용하는 데 큰 어려움이 될 것입니다.

로컬 테스트에 사용된 데이터베이스 버전: Oracle Database 21c Express Edition Release 21.0.0.0.0
온라인 테스트에 사용된 Live SQL 버전: Live SQL 23.3.1, running Oracle Database 19c EE Extreme Perf - 19.17.0.0.0(함정도 있음)
Oracle Spatial 참조 문서 주소: https://docs.oracle.com/en/database/oracle/oracle-database/21/spatl /index.html오픈
소스 지방 및 도시 좌표 경계 데이터(Oracle로 가져올 수 있음): https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov github를 gitee로 대체할 수 있음

2. Oracle은 위도 및 경도 좌표가 경계 내부에 있는지 쿼리합니다.

2.1 쿼리 조건

  • 경계 기하학: POLYGON ((53 20, 52 20, 52 23, 57 23, 57 20, 56 20, 56 22, 53 22, 53 20)), 역오목 문자와 유사
  • 좌표점: POINT (55 21), 이 점은 오목 문자 내부가 아닌 오목 내부에 위치합니다.

경계 기하학 및 좌표점 위치

이 좌표점이 그래프 내부에 있는지 쿼리합니다. 결과는 내부에 없어야 합니다.

2.2 쿼리 결과가 잘못되었습니다. MBR 매칭만 이루어진 것 같습니다.

쿼리 문을 작성합니다. 동일한 그래프를 시계 방향과 시계 반대 방향으로 작성하는 두 가지 방법은 다음과 같습니다.

declare
	-- 定义坐标点
    p SDO_GEOMETRY:=SDO_GEOMETRY('POINT (55 21)',4326);
	-- 定义边界几何图形(顺时针),如果你的WKT是这种,那放到Oracle里面就惨了
    g1 SDO_GEOMETRY:=SDO_GEOMETRY('POLYGON ((53 20, 52 20, 52 23, 57 23, 57 20, 56 20, 56 22, 53 22, 53 20))',4326);
	-- 定义边界几何图形(逆时针),符合Oracle坐标顺序要求
    g2 SDO_GEOMETRY:=SDO_GEOMETRY('POLYGON ((53 20, 53 22, 56 22, 56 20, 57 20, 57 23, 52 23, 52 20, 53 20))',4326);
    v1 clob; v2 clob;
begin
	-- 计算坐标点和2个边界的位置关系
    select SDO_ANYINTERACT(g1,p),SDO_ANYINTERACT(g2,p) into v1,v2 from dual;
    dbms_output.put_line('g1:'||v1 ||'  '|| 'g2:'||v2);
	
	-- 检查2个边界是否有效(ST_IsValid)
	dbms_output.put_line('g1 IsValid:'||SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(g1,0.0000001));
	dbms_output.put_line('g2 IsValid:'||SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(g2,0.0000001));
end;

SQL Developer에서 실행 후 출력 결과(Live SQL의 테스트 결과와 동일):

g1:TRUE  g2:FALSE
g1 IsValid:13367 [Element <1>] [Ring <1>]
g2 IsValid:TRUE

g1시계 방향 경계 WKT 에는 실제로 이 좌표점이 포함되어 있음 을 알 수 g1:TRUE있으며 g1검사의 유효성은 표시된 그래픽이 유효하지 않음을 보여줍니다.

좌표점을 로 변경해 보면 이 좌표는 완전히 오목 문자 외부에 있어 쿼리 결과가 정확하므로 경계 및 좌표 의 위치 계산은 경계 사각형(MBR)에만 일치하고 일치하지 않는다는 POINT (55 10)결론을 내릴 수 있습니다. 정확한 계산이 수행됩니다. 클릭한 MBR 내부에서 일치하는 좌표가 직접 반환되므로 잘못된 결과가 발생합니다.g1ppg1

아니면 SRID를 제거(delete ,4326, 즉 SRID=NULL)하면 쿼리 결과도 맞는데, 이게 굉장히 이상하고 코드가 이상한 방식으로 제대로 실행되고 있는 것입니다. . . 매우 혼란스럽습니다.

동일한 경계 그래픽 WKT와 좌표점을 MySQL, PostgreSQL, SQL Server에서 테스트한 결과 정상입니다. 그래픽이 잘못되는 문제는 없습니다. 단지 Oracle의 문제일 뿐입니다.

2.3 오류 원인

이 오류로 인해 일주일 넘게 괴로움을 겪고 있습니다. SDO_ANYINTERACT및 의 문서에서 그래픽 좌표의 방향에 대한 설명을 찾을 수 없습니다 SDO_RELATE. 제공된 SRID에 문제가 있는 줄 알았습니다(단지 쿼리 결과가 정확했기 때문입니다). SRID=NULL일 ). 다행히 나중에 SDO_ELEM_INFO를 찾았고 Recommendations for Loading and Validating Spatial Data 문서 에서 정확한 답을 얻었습니다 .

단순 폴리곤이 외부인지 내부인지 모르는 경우에만 3을 지정해야 하며, SDO_MIGRATE 패키지(업그레이드)에 설명된 SDO_MIGRATE.TO_CURRENT 프로시저를 사용하여 테이블이나 레이어를 현재 형식으로 업그레이드해야 합니다.

1005: 외부 다각형 링(반시계 방향으로 지정해야 함)
2005: 내부 다각형 링(시계 방향으로 지정해야 함)

방향이 잘못되었거나 ETYPE 또는 GTYPE 값이 잘못된 형상의 경우 잘못된 형상에 SDO_MIGRATE.TO_CURRENT를 사용하여 수정하세요.

이는 Oracle에서 경계 그래픽의 외부 링이 시계 반대 방향이어야 함을 의미합니다. 그런데 SDO_ANYINTERACT시계방향의 에러 그래프에 대해 쿼리를 하면 오라클은 에러를 보고하지 않고 에러 결과를 반환하는데, 이는 어처구니 없는 일이며, 이에 대한 이유는 문서에 설명되어 있지 않습니다. 참고: 다른 몇몇 데이터베이스에는 링 방향 순서가 필요하지 않습니다.

2.4 솔루션

문서에 따르면 방향 순서 요구 사항을 충족하기 위해 SDO_MIGRATE.TO_CURRENT 메서드를 사용하여 SDO_GEOMETRY구성된 경계 형상의 좌표 순서를 처리합니다.

	-- 在原来的基础上,套一层TO_CURRENT()调用,提供一个dim数组即可
    g1 SDO_GEOMETRY:=SDO_MIGRATE.TO_CURRENT(SDO_GEOMETRY('POLYGON ((53 20, 52 20, 52 23, 57 23, 57 20, 56 20, 56 22, 53 22, 53 20))',4326),SDO_DIM_ARRAY(SDO_DIM_ELEMENT('X', -180, 180, 0.0000001),SDO_DIM_ELEMENT('Y', -90, 90, 0.0000001)));

하나의 레이어를 적용한 후에 TO_CURRENT()는 올바른 g1쿼리가 정확하고 그래프의 유효성도 정확해집니다.

3. Oracle에서 SQL로 WKT 초장문 텍스트를 작성하는 방법

3.1 Oracle에서 매우 긴 텍스트가 포함된 SQL을 실행할 때 오류가 발생합니다.

다음 코드를 수정하고 실행합니다.

-- 字符串里面放50k的字符串,可浏览器控制台里面执行js得到长文本: new Array(50001).join("a")
select '这里放50000个字符......' from dual;

SQL Developer에서 직접 오류를 보고합니다(라이브 SQL 지원은 더욱 나쁩니다).

ORA-01704: 字符串文字太长
01704. 00000 -  "string literal too long"
*Cause:    The string literal is longer than 4000 characters.
*Action:   Use a string literal of at most 4000 characters.
           Longer values may only be entered using bind variables.

안녕하세요 여러분, SQL 문에서 문자열 길이에 대한 제한을 본 것은 이번이 처음입니다. 문서를 살펴보고 PL/SQL Program Limits 를 찾았습니다 . size of a string literal (bytes): 32767다른 데이터베이스에서는 이 문제가 발생한 적이 없습니다. Longer values may only be entered using bind variables.제안된 솔루션은 시각적으로 프로그래밍 환경의 ReadyStatement를 대상으로 하며, 이미 SQL을 작성하고 SQL 파일에 해머 변수를 연결했습니다.

처음에 언급했듯이 복잡한 지리적 좌표 경계 그래픽의 WKT에는 수백 KB, 심지어 수 MB의 매우 긴 텍스트가 포함될 수 있으며, 다른 데이터베이스도 유사한 방법을 통해 간단히 삽입을 완료할 수 있습니다. 이제 Oracle은 이를 수행해야 합니다 insert tab values('100kb wkt'). 쓰기 문자열의 길이 제한에 따라 추가로 색다른 코드를 작성하십시오.

3.2 CLOB를 사용하여 무한 연결하여 매우 긴 텍스트 얻기

Oracle CLOB유형은 텍스트를 수용할 수 4GB있으므로 너무 긴 WKT 텍스트를 짧은 텍스트 섹션으로 나누고 단일 문자열이 너무 길어지는 것을 방지하기 위해 clob 변수로 엮어 이 문제를 해결할 수 있습니다.

declare txt clob:='';begin
	txt:=txt||'POLYGON(( .... WKT切分的2KB字符串';
	txt:=txt||'.... WKT切分的2KB字符串';
	txt:=txt||'.... WKT切分的2KB字符串';
	-- ...... 剩余的2KB字符串
	
	dbms_output.put_line('txt: '||dbms_lob.getlength(txt));
	
	-- 把超长WKT文本转成几何空间对象,修正坐标方向,插入数据库
	-- INSERT INTO tableName(polygon) VALUES( SDO_MIGRATE.TO_CURRENT(SDO_GEOMETRY(txt,4326),SDO_DIM_ARRAY(SDO_DIM_ELEMENT('X', -180, 180, 0.0000001),SDO_DIM_ELEMENT('Y', -90, 90, 0.0000001))) );
end;
/

SQL이 Oracle의 다양한 터미널(SQLPlus, SQL Developer)에서 작성할 수 있는 단일 문자열의 길이는 3k에서 32k까지로 혼동되기 때문에 호환성을 극대화하기 위해 2KB 길이의 세그먼트로 스플라이싱하여 긴 문자열을 지원합니다. 직접 접합할 수 있습니다. , || 사용할 필요가 없습니다 to_clob.dbms_lob.append

참고: Live SQL에서 테스트할 때 SQL 문 전체 길이가 32KB(단일 문자열 아님)를 초과하면 쿼리에서 오류가 보고되거나 결과가 전혀 반환되지 않는데, 이는 SQLPlus 및 SQL의 성능과 완전히 다릅니다. 개발자; 매우 긴 텍스트를 전달하는 현재 라이브 테스트는 SQL에서 불가능하다는 것을 이해할 수 있습니다.

3.3 오픈 소스 지방 및 시 좌표 경계 WKT 데이터 가져오기

오픈 소스 라이브러리인 AreaCity-JsSpider-StatsGov의 문서에서 최신 주, 시, 3차 또는 타운십 수준 데이터를 얻으세요. shp, geojson 및 sql 형식으로 지원됩니다. 내보내기만 선택하면 됩니다. Oracle 형식의 SQL 파일입니다. 에서SQLPlus

@"D:/xxx/xxx.sql"; --改成实际的文件路径

SQL 파일 내 매우 긴 WKT 텍스트를 위의 2KB 섹션의 길이에 따라 분할한 뒤 CLOB를 이용해 스플라이스(splicing)한다. 이 SQL 파일의 끝 부분에 인덱스가 자동으로 생성되거나 다음 내용에 따라 수동으로 인덱스를 생성할 수도 있습니다. 인덱스가 생성된 후에는 쿼리 SDO_ANYINTERACT속도가 100배 빨라집니다.

4. Oracle 데이터베이스의 공간 인덱스 및 쿼리는 WKT 매우 긴 텍스트를 반환합니다.

4.1 쿼리 가속화: Oracle 데이터베이스의 공간 인덱스

공간 인덱스 문서: 공간 데이터 인덱싱 및 쿼리user_sdo_geom_metadata , 이 테이블과 관련된 이 테이블은 경도와 위도 범위 및 SRID만 저장하지만 매우 중요합니다!

user_sdo_geom_metadata내부에 구성된 SRID가 테이블로 가져온 데이터의 SRID와 일치하지 않으면 공간 쿼리 중에 오류가 직접 보고됩니다 . 데이터의 SRID가 NULL인 경우 메타데이터의 SRID도 NULL이어야 하며, 다른 SRID도 기존 SRID 값이어야 합니다( select * from MDSYS.CS_SRS order by srid모든 SRID를 쿼리하여).

그런 다음 정상적으로 인덱스를 생성합니다.

-- 先往user_sdo_geom_metadata里面插入配置数据
insert into user_sdo_geom_metadata(TABLE_NAME,COLUMN_NAME,DIMINFO,SRID)VALUES (upper('tableName'),upper('polygon'),SDO_DIM_ARRAY(SDO_DIM_ELEMENT('X', -180, 180, 0.0000001),SDO_DIM_ELEMENT('Y', -90, 90, 0.0000001)),NULL); -- SRID=NULL 或 一个具体值

-- 创建空间索引
create index indexName ON tableName(polygon) INDEXTYPE IS MDSYS.SPATIAL_INDEX_V2;

테스트 결과, 테이블 이름을 수정하면 메타데이터의 테이블 이름이 자동으로 수정되며, 테이블을 삭제해도 메타데이터의 테이블 관련 구성은 삭제되지 않는 것으로 나타났습니다.

테이블에 공간 데이터의 양이 많을 경우, 공간 인덱스 추가에 따른 쿼리 속도 최적화 효과가 매우 상세하며, 위에서 가져온 지방 및 시의 3단계 경계 데이터에는 3,600개 이상의 조각이 포함되어 있습니다. , 좌표 하나를 쿼리하는 데 7초가 걸리며, 인덱싱 후 한 번 쿼리하는 데 0.06초밖에 걸리지 않아 쿼리 성능이 100배 향상됩니다.

4.2 SDO_GEOMETRY에서 반환된 WKT 매우 긴 텍스트를 쿼리하는 방법은 무엇입니까?

CLOB spliced ​​SQL 문을 통해 매우 긴 WKT 텍스트를 데이터베이스에 삽입한 후 SQL 쿼리 문을 통해서만 경계 기하학 객체에서 WKT 텍스트를 읽는 것도 또 다른 문제인 것 같습니다.

select SDO_GEOMETRY.GET_WKT(polygon) from tableName;

GET_WKT이 메서드는 종종 오류를 보고합니다(가끔 정상적으로 발견될 수도 있음).

ORA-13199: wk buffer merge failure
ORA-06512: 在 "MDSYS.SDO_UTIL", line 857
ORA-06512: 在 "MDSYS.SDO_UTIL", line 896
ORA-06512: 在 "MDSYS.SDO_GEOMETRY", line 36

현재 SDO_GEOMETRY의 WKT 초장문 텍스트를 안정적으로 쿼리할 수 있는 방법이 없어 오라클에서는 어려운 일이다.

참고: 위의 문제(어려움) 중 어느 것도 MySQL, PostgreSQL 또는 SQLServer에 적용되지 않습니다. 이런 식으로 =를 선택하는 방법을 알아야 합니다. =

【위에】

추천

출처blog.csdn.net/xiangyuecn/article/details/132401448