sql 필드 해석 기 구현 예제
sql 의 필드 목록 때문에 사용 방식 이 제한 되 어 있 습 니 다.예 를 들 어 a as b,a,a...
1.문제 풀이 사고방식
복잡 한 처 리 를 하지 않 으 려 면 가장 생각 하기 쉬 운 것 은 바로 어떤 특징 으로 분할 하면 된다 는 것 이다.예 를 들 어 필드 목록 부분 을 먼저 캡 처 한 다음 에 쉼표','분할 하면 하나의 필드 를 얻 을 수 있 습 니 다.그리고 세분 화 를 해 야 합 니 다.사실은 as 로 분할 하면 됩 니 다.
가능 해 보이 지만 많은 구멍 이 존재 합 니 다.우선,이 안 에는 너무 많은 가설 이 있 습 니 다.각종 절 취 된 부분 은 요구 에 부합 되 어야 하고 불필요 한 쉼표 가 없어 야 하 며 as 등 이 있어 야 합 니 다.이것 은 분명히 요구 에 부합 되 지 않 는 다.
둘째,우 리 는 전환 방식 을 바 꿀 수 있다.예 를 들 어 field 부분 을 먼저 캡 처 한 다음 에 as 로 분할 한 다음 에 쉼표 로 분할 한 다음 에 마지막 단 어 를 field 로 한다.
더 나 빠 진 것 같 아 요.어디 까지 캡 처 했 는 지 전혀 모 르 겠 어 요.즉,원문 이 거의 파괴 되 었 고 as 변환 라벨 이 있어 야 하 며 함수 가 as 가 있 는 장면 을 노 리 는 것 은 완전히 잘못된 것 이다.
셋째,스스로 한 단어 씩 해석 하 는 것 이 좋 습 니 다.field 필드 는 몇 가지 상황 밖 에 없습니다.1.일반 필드 는 select a 와 같 습 니 다.2.selecta as b 와 같은 as 가 있 는 일반 필드;3.select coalesce(a,b)와 같은 함수 필드;4.함 수 를 가지 고 as 를 가 진 필드,예 를 들 어 select coalesce(a,b)ab;5.select cast(a as string)b 와 같은 함수 내 as 필드;... 우 리 는 차례대로 대응 하 는 상황 을 열거 하기 만 하면 필드 를 해석 할 수 있다.
좋 은 생각 인 것 같 습 니 다.하지만 구체 적 으로 실현 하 는 것 은 어 떨 까?
2.구체 적 해석 실현
주로 두 부분 으로 나 뉘 는데 1.분석 후의 결과 데이터 구 조 를 정의 하여 필드 정 보 를 명확 하 게 묘사 해 야 한다.2.단 어 를 나 누 어 sql 을 분석 하고 구조 체 로 되 돌려 줍 니 다.
우 리 는 먼저 전체 알고리즘 의 핵심 을 살 펴 보 자.
/**
* : sql
*
* 1:
* select COALESCE(t1.xno, t2.xno, t3.xno) as xno,
* case when t1.no is not null then 1 else null end as xxk001,
* case when t2.no is not null then 1 else null end as xxk200,
* case when t3.xno is not null then 1 else null end as xx3200
* from xxk001 t1
* full join xxkj100 t2 on t1.xno = t2.xno
* full join xxkj200 t3 on t1.xno = t3.xno;
*
* 2:
* select cast(a as string) as b from ccc;
*
* 3:
* with a as(select cus,x1 from b1), b as (select cus,x2 from b2)
* select a.cus as a_cus from a join b on a.cus=b.cus where xxx;
*
* 4:
* select a.xno,b.xx from a_tb as a join b_tb as b on a.id = b.id
*
* 5:
* select cast \t(a as string) a_str, cc (a as double) a_double from x
*
*/
public class SimpleSqlFieldParser {
/**
* sql
*
* @param sql sql, select xx from xxx join ...
* @return
*/
public static List<SelectFieldClauseDescriptor> parse(String sql) {
String columnPart = adaptFieldPartSql(sql);
int deep = 0;
List<StringBuilder> fieldTokenSwap = new ArrayList<>();
StringBuilder currentTokenBuilder = new StringBuilder();
List<SelectFieldClauseDescriptor> fieldList = new ArrayList<>();
fieldTokenSwap.add(currentTokenBuilder);
int len = columnPart.length();
char[] columnPartChars = columnPart.toCharArray();
for(int i = 0; i < len; i++) {
// , ,tab
//
// ( ,++deep;
// ) ,--deep;
// deep>0
// as fieldName
// case end ;
//, token,
char currentChar = columnPartChars[i];
switch (currentChar) {
case '(':
++deep;
currentTokenBuilder.append(currentChar);
break;
case ')':
--deep;
currentTokenBuilder.append(currentChar);
break;
case ',':
if(deep == 0) {
addNewField(fieldList, fieldTokenSwap, true);
fieldTokenSwap = new ArrayList<>();
currentTokenBuilder = new StringBuilder();
fieldTokenSwap.add(currentTokenBuilder);
break;
}
currentTokenBuilder.append(currentChar);
break;
case ' ':
case '\t':
case '\r':
case '
':
if(deep > 0) {
currentTokenBuilder.append(currentChar);
continue;
}
if(currentTokenBuilder.length() == 0) {
continue;
}
// original_name as --> alias
if(i + 1 < len) {
int j = i + 1;
//
StringBuilder spaceHolder = new StringBuilder();
boolean isNextLeftBracket = false;
do {
char nextChar = columnPart.charAt(j++);
if(nextChar == ' ' || nextChar == '\t'
|| nextChar == '\r' || nextChar == '
') {
spaceHolder.append(nextChar);
continue;
}
if(nextChar == '(') {
isNextLeftBracket = true;
}
break;
} while (j < len);
if(isNextLeftBracket) {
currentTokenBuilder.append(currentChar);
}
if(spaceHolder.length() > 0) {
currentTokenBuilder.append(spaceHolder);
i += spaceHolder.length();
}
if(isNextLeftBracket) {
// continue next for, function begin
continue;
}
}
if(fieldTokenSwap.size() == 1) {
if(fieldTokenSwap.get(0).toString().equalsIgnoreCase("case")) {
String caseWhenPart = CommonUtil.readSplitWord(
columnPartChars, i, " ", "end");
currentTokenBuilder.append(caseWhenPart);
if(caseWhenPart.length() <= 0) {
throw new BizException(" , case..when ");
}
i += caseWhenPart.length();
}
}
addNewField(fieldList, fieldTokenSwap, false);
currentTokenBuilder = new StringBuilder();
fieldTokenSwap.add(currentTokenBuilder);
break;
//
default:
currentTokenBuilder.append(currentChar);
break;
}
}
//
addNewField(fieldList, fieldTokenSwap, true);
return fieldList;
}
/**
*
*
* @param fieldList
* @param fieldTokenSwap
*/
private static void addNewField(List<SelectFieldClauseDescriptor> fieldList,
List<StringBuilder> fieldTokenSwap,
boolean forceAdd) {
int ts = fieldTokenSwap.size();
if(ts == 1 && forceAdd) {
// db.original_name,
String fieldName = fieldTokenSwap.get(0).toString();
String alias = fieldName;
if(fieldName.contains(".")) {
alias = fieldName.substring(fieldName.lastIndexOf('.') + 1);
}
fieldList.add(new SelectFieldClauseDescriptor(fieldName, alias));
return;
}
if(ts < 2) {
return;
}
if(ts == 2) {
// original_name alias,
if(fieldTokenSwap.get(1).toString().equalsIgnoreCase("as")) {
return;
}
fieldList.add(new SelectFieldClauseDescriptor(
fieldTokenSwap.get(0).toString(),
fieldTokenSwap.get(1).toString()));
}
else if(ts == 3) {
// original_name as alias,
fieldList.add(new SelectFieldClauseDescriptor(
fieldTokenSwap.get(0).toString(),
fieldTokenSwap.get(2).toString()));
}
else {
throw new BizException(" , 3 :" + ts);
}
}
// field
private static String adaptFieldPartSql(String fullSql) {
int start = fullSql.lastIndexOf("select ");
int end = fullSql.lastIndexOf(" from");
String columnPart = fullSql.substring(start + "select ".length(), end);
return columnPart.trim();
}
}
비교적 간단 하 다 고 해 야 지,for 하나,switch 하나 면 돼.다른 것 은 논리 적 판정 이 더 많다.다음은 필드 설명 류 의 쓰기 방법 을 살 펴 보 겠 습 니 다.사실은 두 필드,소스 필드 와 별명 입 니 다.
/**
* : sql select
*
*/
public class SelectFieldClauseDescriptor {
private String fieldName;
private String alias;
public SelectFieldClauseDescriptor(String fieldName, String alias) {
this.fieldName = fieldName;
this.alias = alias;
}
public String getFieldName() {
return fieldName;
}
public String getAlias() {
return alias;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SelectFieldClauseDescriptor that = (SelectFieldClauseDescriptor) o;
return Objects.equals(fieldName, that.fieldName) &&
Objects.equals(alias, that.alias);
}
@Override
public int hashCode() {
return Objects.hash(fieldName, alias);
}
@Override
public String toString() {
return "SelectFieldClauseDescriptor{" +
"fieldName='" + fieldName + '\'' +
", alias='" + alias + '\'' +
'}';
}
}
그것 이 존재 하 는 의 미 는 단지 사용자 가 더욱 편리 하 게 가 치 를 얻 기 위해 더욱 진일보 한 해석 에 근 거 를 제공 했다 고 생각 하 는 것 이다.3.유닛 테스트
사실 이런 도구 류 를 쓰 는 것 처럼 단원 테스트 가 가장 편리 하고 간단 하 다.최초의 결과 로,우 리 는 테스트 구동 개발 이 가장 적합 할 것 이 라 고 이미 예 상 했 기 때문이다.그리고 기본적으로 예상 치 에 맞지 않 는 값 이 나 오 면 빠르게 포 지 셔 닝 문제 가 생 긴 다.
/**
* : sql
**/
public class SimpleSqlFieldParserTest {
@Test
public void testParse() {
String sql;
List<SelectFieldClauseDescriptor> parsedFieldList;
sql = "select COALESCE(t1.xno, t2.xno, t3.xno) as xno,
" +
" case when t1.xno is not null then 1 else null end as xxk001,
" +
" case when t2.xno is not null then 1 else null end as xxk200,
" +
" case when t3.xno is not null then 1 else null end as xx3200
" +
" from xxk001 t1
" +
" full join xxkj100 t2 on t1.xno = t2.xno
" +
" full join xxkj200 t3 on t1.xno = t3.xno;";
parsedFieldList = SimpleSqlFieldParser.parse(sql);
System.out.println("result:");
parsedFieldList.forEach(System.out::println);
Assert.assertEquals(" ",
4, parsedFieldList.size());
Assert.assertEquals(" ",
"xno", parsedFieldList.get(0).getAlias());
Assert.assertEquals(" ",
"xx3200", parsedFieldList.get(3).getAlias());
sql = "select cast(a as string) as b from ccc;";
parsedFieldList = SimpleSqlFieldParser.parse(sql);
System.out.println("result:");
parsedFieldList.forEach(System.out::println);
Assert.assertEquals(" ",
1, parsedFieldList.size());
Assert.assertEquals(" ",
"b", parsedFieldList.get(0).getAlias());
sql = "with a as(select cus,x1 from b1), b as (select cus,x2 from b2)
" +
" select a.cus as a_cus, cast(a
as string) as a_cus2, " +
"b.x2 b2 from a join b on a.cus=b.cus where xxx;";
parsedFieldList = SimpleSqlFieldParser.parse(sql);
System.out.println("result:");
parsedFieldList.forEach(System.out::println);
Assert.assertEquals(" ",
3, parsedFieldList.size());
Assert.assertEquals(" ",
"a_cus", parsedFieldList.get(0).getAlias());
Assert.assertEquals(" ",
"b2", parsedFieldList.get(2).getAlias());
sql = "select a.xno,b.xx,qqq from a_tb as a join b_tb as b on a.id = b.id";
parsedFieldList = SimpleSqlFieldParser.parse(sql);
System.out.println("result:");
parsedFieldList.forEach(System.out::println);
Assert.assertEquals(" ",
3, parsedFieldList.size());
Assert.assertEquals(" ",
"xno", parsedFieldList.get(0).getAlias());
Assert.assertEquals(" ",
"qqq", parsedFieldList.get(2).getAlias());
sql = "select cast (a.a_int as string) a_str, b.xx, coalesce
( a, b, c) qqq from a_tb as a join b_tb as b on a.id = b.id";
parsedFieldList = SimpleSqlFieldParser.parse(sql);
System.out.println("result:");
parsedFieldList.forEach(System.out::println);
Assert.assertEquals(" ",
3, parsedFieldList.size());
Assert.assertEquals(" ",
"a_str", parsedFieldList.get(0).getAlias());
Assert.assertEquals(" ",
"cast (a.a_int as string)", parsedFieldList.get(0).getFieldName());
Assert.assertEquals(" ",
"qqq", parsedFieldList.get(2).getAlias());
Assert.assertEquals(" ",
"coalesce
( a, b, c)", parsedFieldList.get(2).getFieldName());
}
}
이로써 간단 한 필드 해석 기 가 완성 되 었 습 니 다.작은 도구,참고 하 세 요!sql 필드 해석 기 구현 예제 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 sql 필드 해석 기 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 지원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
깊이 중첩된 객체를 정확히 일치 검색 - PostgreSQL목차 * 🚀 * 🎯 * 🏁 * 🙏 JSON 객체 예시 따라서 우리의 현재 목표는 "고용주"사용자가 입력한 검색어(이 경우에는 '요리')를 얻고 이 용어와 정확히 일치하는 모든 사용자 프로필을 찾는 것입니다. 즐거운 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.