SQLGlot은 종속성이 없는 SQL 파서, 트랜스파일러, 최적화 프로그램 및 엔진입니다. SQL 형식을 지정하거나 DuckDB, Presto/Trino, Spark/Databricks, Snowflake 및 BigQuery와 같은 24개의 다양한 방언을 변환하는 데 사용할 수 있습니다. 다양한 SQL 입력 및 출력을 읽고 대상 방언에서 구문론적, 의미론적으로 올바른 SQL을 읽는 것을 목표로 합니다.
이는 강력한 테스트 스위트를 갖춘 매우 포괄적인 일반 SQL 파서입니다. 순수하게 Python으로 작성되었지만 성능도 매우 뛰어납니다.
파서를 쉽게 사용자 정의하고, 쿼리를 분석하고, 표현식 트리를 탐색하고, 프로그래밍 방식으로 SQL을 작성할 수 있습니다.
구문 오류가 강조 표시되고 방언 비호환성은 구성에 따라 경고하거나 발생할 수 있습니다. 그러나 SQLGlot은 SQL 유효성 검사기를 목표로 하지 않으므로 특정 구문 오류를 감지하지 못할 수 있습니다.
API 설명서와 표현식 트리 입문서에서 SQLGlot에 대해 자세히 알아보세요.
SQLGlot에서는 기여를 매우 환영합니다. 시작하려면 기여 가이드와 온보딩 문서를 읽어보세요!
PyPI에서:
pip3 install " sqlglot[rs] "
# Without Rust tokenizer (slower):
# pip3 install sqlglot
또는 지역 결제를 통해:
make install
개발 요구 사항(선택 사항):
make install-dev
버전 번호 MAJOR
가 주어졌습니다. MINOR
. PATCH
, SQLGlot은 다음 버전 관리 전략을 사용합니다.
PATCH
버전은 이전 버전과 호환되는 수정 사항이나 기능 추가가 있을 때 증가됩니다.MINOR
버전은 이전 버전과 호환되지 않는 수정 사항이나 기능 추가가 있을 때 증가됩니다.MAJOR
버전은 이전 버전과 호환되지 않는 중요한 수정 사항이나 기능 추가가 있는 경우 증가합니다. 우리는 당신의 의견을 듣고 싶습니다. 커뮤니티 Slack 채널에 참여하세요!
유효해야 하는 SQL을 구문 분석하려고 했지만 실패했습니다. 왜 이런 일이 발생했습니까?
parse_one(sql, dialect="spark")
(또는 read="spark"
). 방언이 지정되지 않은 경우, parse_one
지원되는 모든 방언의 상위 집합으로 설계된 "SQLGlot 방언"에 따라 쿼리를 구문 분석하려고 시도합니다. 방언을 지정해 보았으나 여전히 작동하지 않으면 문제를 제기해 주세요.SQL을 출력하려고 했지만 올바른 방언이 아닙니다!
parse_one(sql, dialect="spark").sql(dialect="duckdb")
(또는 transpile(sql, read="spark", write="duckdb")
. transpile(sql, read="spark", write="duckdb")
).잘못된 SQL을 구문 분석하려고 시도했는데 오류가 발생하더라도 작동했습니다! 내 SQL의 유효성을 검사하지 않은 이유는 무엇입니까?
sqlglot.dataframe은 어떻게 되었나요?
한 방언에서 다른 방언으로 쉽게 번역할 수 있습니다. 예를 들어 날짜/시간 함수는 방언에 따라 다르며 처리하기 어려울 수 있습니다.
import sqlglot
sqlglot . transpile ( "SELECT EPOCH_MS(1618088028295)" , read = "duckdb" , write = "hive" )[ 0 ]
' SELECT FROM_UNIXTIME(1618088028295 / POW(10, 3)) '
SQLGlot은 사용자 정의 시간 형식을 변환할 수도 있습니다.
import sqlglot
sqlglot . transpile ( "SELECT STRFTIME(x, '%y-%-m-%S')" , read = "duckdb" , write = "hive" )[ 0 ]
" SELECT DATE_FORMAT(x, 'yy-M-ss') "
식별자 구분 기호 및 데이터 유형도 번역될 수 있습니다.
import sqlglot
# Spark SQL requires backticks (`) for delimited identifiers and uses `FLOAT` over `REAL`
sql = """WITH baz AS (SELECT a, c FROM foo WHERE a = 1) SELECT f.a, b.b, baz.c, CAST("b"."a" AS REAL) d FROM foo f JOIN bar b ON f.a = b.a LEFT JOIN baz ON f.a = baz.a"""
# Translates the query into Spark SQL, formats it, and delimits all of its identifiers
print ( sqlglot . transpile ( sql , write = "spark" , identify = True , pretty = True )[ 0 ])
WITH ` baz ` AS (
SELECT
` a ` ,
` c `
FROM ` foo `
WHERE
` a ` = 1
)
SELECT
` f ` . ` a ` ,
` b ` . ` b ` ,
` baz ` . ` c ` ,
CAST( ` b ` . ` a ` AS FLOAT) AS ` d `
FROM ` foo ` AS ` f `
JOIN ` bar ` AS ` b `
ON ` f ` . ` a ` = ` b ` . ` a `
LEFT JOIN ` baz `
ON ` f ` . ` a ` = ` baz ` . ` a `
댓글도 최선을 다해 보존됩니다.
sql = """
/* multi
line
comment
*/
SELECT
tbl.cola /* comment 1 */ + tbl.colb /* comment 2 */,
CAST(x AS SIGNED), # comment 3
y -- comment 4
FROM
bar /* comment 5 */,
tbl # comment 6
"""
# Note: MySQL-specific comments (`#`) are converted into standard syntax
print ( sqlglot . transpile ( sql , read = 'mysql' , pretty = True )[ 0 ])
/* multi
line
comment
*/
SELECT
tbl . cola /* comment 1 */ + tbl . colb /* comment 2 */ ,
CAST(x AS INT ), /* comment 3 */
y /* comment 4 */
FROM bar /* comment 5 */ , tbl /* comment 6 */
표현식 도우미를 사용하여 SQL을 탐색하여 쿼리에서 열 및 테이블 찾기와 같은 작업을 수행할 수 있습니다.
from sqlglot import parse_one , exp
# print all column references (a and b)
for column in parse_one ( "SELECT a, b + 1 AS c FROM d" ). find_all ( exp . Column ):
print ( column . alias_or_name )
# find all projections in select statements (a and c)
for select in parse_one ( "SELECT a, b + 1 AS c FROM d" ). find_all ( exp . Select ):
for projection in select . expressions :
print ( projection . alias_or_name )
# find all tables (x, y, z)
for table in parse_one ( "SELECT * FROM x JOIN y JOIN z" ). find_all ( exp . Table ):
print ( table . name )
SQLGlot의 내부 구조에 대해 자세히 알아보려면 ast 입문서를 읽어보세요.
파서는 구문에서 오류를 감지하면 ParseError
발생시킵니다.
import sqlglot
sqlglot . transpile ( "SELECT foo FROM (SELECT baz FROM t" )
sqlglot.errors.ParseError: Expecting ). Line 1, Col: 34.
SELECT foo FROM (SELECT baz FROM t
~
프로그래밍 방식으로 구조화된 구문 오류에 액세스할 수 있습니다.
import sqlglot
try :
sqlglot . transpile ( "SELECT foo FROM (SELECT baz FROM t" )
except sqlglot . errors . ParseError as e :
print ( e . errors )
[{
'description' : 'Expecting )' ,
'line' : 1 ,
'col' : 34 ,
'start_context' : 'SELECT foo FROM (SELECT baz FROM ' ,
'highlight' : 't' ,
'end_context' : '' ,
'into_expression' : None
}]
특정 방언 간에는 일부 쿼리를 번역하지 못할 수도 있습니다. 이러한 경우 SQLGlot은 경고를 표시할 수 있으며 기본적으로 최선의 변환을 진행합니다.
import sqlglot
sqlglot . transpile ( "SELECT APPROX_DISTINCT(a, 0.1) FROM foo" , read = "presto" , write = "hive" )
APPROX_COUNT_DISTINCT does not support accuracy
' SELECT APPROX_COUNT_DISTINCT(a) FROM foo '
이 동작은 unsupported_level
속성을 설정하여 변경할 수 있습니다. 예를 들어 RAISE
또는 IMMEDIATE
로 설정하여 대신 예외가 발생하도록 할 수 있습니다.
import sqlglot
sqlglot . transpile ( "SELECT APPROX_DISTINCT(a, 0.1) FROM foo" , read = "presto" , write = "hive" , unsupported_level = sqlglot . ErrorLevel . RAISE )
sqlglot.errors.UnsupportedError: APPROX_COUNT_DISTINCT does not support accuracy
참조된 테이블의 스키마와 같이 정확하게 변환하기 위해 추가 정보가 필요한 쿼리가 있습니다. 이는 특정 변환이 유형을 구분하기 때문입니다. 즉, 해당 의미를 이해하려면 유형 추론이 필요하기 때문입니다. qualify
및 annotate_types
최적화 규칙이 이에 도움이 될 수 있지만 상당한 오버헤드와 복잡성을 추가하기 때문에 기본적으로 사용되지 않습니다.
변환은 일반적으로 어려운 문제이므로 SQLGlot은 이 문제를 해결하기 위해 "증분" 접근 방식을 사용합니다. 즉, 현재 일부 입력에 대한 지원이 부족한 방언 쌍이 있을 수 있지만 이는 시간이 지나면서 개선될 것으로 예상됩니다. 잘 문서화되고 테스트된 문제나 PR을 높이 평가하므로 지침이 필요하면 언제든지 문의하세요!
SQLGlot은 SQL 표현식을 점진적으로 구축하는 것을 지원합니다.
from sqlglot import select , condition
where = condition ( "x=1" ). and_ ( "y=1" )
select ( "*" ). from_ ( "y" ). where ( where ). sql ()
' SELECT * FROM y WHERE x = 1 AND y = 1 '
구문 분석된 트리를 수정하는 것이 가능합니다.
from sqlglot import parse_one
parse_one ( "SELECT x FROM y" ). from_ ( "z" ). sql ()
' SELECT x FROM z '
구문 분석된 표현식은 트리의 각 노드에 매핑 함수를 적용하여 재귀적으로 변환할 수도 있습니다.
from sqlglot import exp , parse_one
expression_tree = parse_one ( "SELECT a FROM x" )
def transformer ( node ):
if isinstance ( node , exp . Column ) and node . name == "a" :
return parse_one ( "FUN(a)" )
return node
transformed_tree = expression_tree . transform ( transformer )
transformed_tree . sql ()
' SELECT FUN(a) FROM x '
SQLGlot은 쿼리를 "최적화된" 형식으로 다시 작성할 수 있습니다. 새로운 표준 AST를 생성하기 위해 다양한 기술을 수행합니다. 이 AST는 쿼리를 표준화하거나 실제 엔진 구현을 위한 기반을 제공하는 데 사용될 수 있습니다. 예를 들어:
import sqlglot
from sqlglot . optimizer import optimize
print (
optimize (
sqlglot . parse_one ( """
SELECT A OR (B OR (C AND D))
FROM x
WHERE Z = date '2021-01-01' + INTERVAL '1' month OR 1 = 0
""" ),
schema = { "x" : { "A" : "INT" , "B" : "INT" , "C" : "INT" , "D" : "INT" , "Z" : "STRING" }}
). sql ( pretty = True )
)
SELECT
(
" x " . " a " <> 0 OR " x " . " b " <> 0 OR " x " . " c " <> 0
)
AND (
" x " . " a " <> 0 OR " x " . " b " <> 0 OR " x " . " d " <> 0
) AS " _col_0 "
FROM " x " AS " x "
WHERE
CAST( " x " . " z " AS DATE ) = CAST( ' 2021-02-01 ' AS DATE )
repr
호출하여 구문 분석된 SQL의 AST 버전을 볼 수 있습니다.
from sqlglot import parse_one
print ( repr ( parse_one ( "SELECT a + 1 AS z" )))
Select (
expressions = [
Alias (
this = Add (
this = Column (
this = Identifier ( this = a , quoted = False )),
expression = Literal ( this = 1 , is_string = False )),
alias = Identifier ( this = z , quoted = False ))])
SQLGlot은 소스 표현식을 대상 표현식으로 변환하는 데 필요한 일련의 작업 형식으로 두 표현식 간의 의미적 차이와 출력 변경 사항을 계산할 수 있습니다.
from sqlglot import diff , parse_one
diff ( parse_one ( "SELECT a + b, c, d" ), parse_one ( "SELECT c, a - b, d" ))
[
Remove ( expression = Add (
this = Column (
this = Identifier ( this = a , quoted = False )),
expression = Column (
this = Identifier ( this = b , quoted = False )))),
Insert ( expression = Sub (
this = Column (
this = Identifier ( this = a , quoted = False )),
expression = Column (
this = Identifier ( this = b , quoted = False )))),
Keep (
source = Column ( this = Identifier ( this = a , quoted = False )),
target = Column ( this = Identifier ( this = a , quoted = False ))),
...
]
SQL에 대한 의미 차이도 참조하세요.
Dialect
를 서브클래싱하여 방언을 추가할 수 있습니다.
from sqlglot import exp
from sqlglot . dialects . dialect import Dialect
from sqlglot . generator import Generator
from sqlglot . tokens import Tokenizer , TokenType
class Custom ( Dialect ):
class Tokenizer ( Tokenizer ):
QUOTES = [ "'" , '"' ]
IDENTIFIERS = [ "`" ]
KEYWORDS = {
** Tokenizer . KEYWORDS ,
"INT64" : TokenType . BIGINT ,
"FLOAT64" : TokenType . DOUBLE ,
}
class Generator ( Generator ):
TRANSFORMS = { exp . Array : lambda self , e : f"[ { self . expressions ( e ) } ]" }
TYPE_MAPPING = {
exp . DataType . Type . TINYINT : "INT64" ,
exp . DataType . Type . SMALLINT : "INT64" ,
exp . DataType . Type . INT : "INT64" ,
exp . DataType . Type . BIGINT : "INT64" ,
exp . DataType . Type . DECIMAL : "NUMERIC" ,
exp . DataType . Type . FLOAT : "FLOAT64" ,
exp . DataType . Type . DOUBLE : "FLOAT64" ,
exp . DataType . Type . BOOLEAN : "BOOL" ,
exp . DataType . Type . TEXT : "STRING" ,
}
print ( Dialect [ "custom" ])
<class '__main__.Custom'>
SQLGlot은 테이블이 Python 사전으로 표시되는 SQL 쿼리를 해석할 수 있습니다. 엔진은 빠르지는 않지만 단위 테스트 및 Python 개체 전체에서 기본적으로 SQL을 실행하는 데 유용할 수 있습니다. 또한 이 기반은 Arrow 및 Pandas와 같은 빠른 컴퓨팅 커널과 쉽게 통합될 수 있습니다.
아래 예에서는 집계 및 조인과 관련된 쿼리 실행을 보여줍니다.
from sqlglot . executor import execute
tables = {
"sushi" : [
{ "id" : 1 , "price" : 1.0 },
{ "id" : 2 , "price" : 2.0 },
{ "id" : 3 , "price" : 3.0 },
],
"order_items" : [
{ "sushi_id" : 1 , "order_id" : 1 },
{ "sushi_id" : 1 , "order_id" : 1 },
{ "sushi_id" : 2 , "order_id" : 1 },
{ "sushi_id" : 3 , "order_id" : 2 },
],
"orders" : [
{ "id" : 1 , "user_id" : 1 },
{ "id" : 2 , "user_id" : 2 },
],
}
execute (
"""
SELECT
o.user_id,
SUM(s.price) AS price
FROM orders o
JOIN order_items i
ON o.id = i.order_id
JOIN sushi s
ON i.sushi_id = s.id
GROUP BY o.user_id
""" ,
tables = tables
)
user_id price
1 4.0
2 3.0
참조: 처음부터 Python SQL 엔진 작성.
SQLGlot은 pdoc를 사용하여 API 문서를 제공합니다.
호스팅 버전은 SQLGlot 웹사이트에 있거나 다음을 사용하여 로컬로 구축할 수 있습니다.
make docs-serve
make style # Only linter checks
make unit # Only unit tests (or unit-rs, to use the Rust tokenizer)
make test # Unit and integration tests (or test-rs, to use the Rust tokenizer)
make check # Full test suite & linter checks
벤치마크는 Python 3.10.12에서 몇 초 만에 실행됩니다.
질문 | sqlglot | sqlglotrs | SQL플러프 | sqltree | SQLparse | moz_sql_parser | SQL산화물 |
---|---|---|---|---|---|---|---|
티치 | 0.00944 (1.0) | 0.00590 (0.625) | 0.32116 (33.98) | 0.00693 (0.734) | 0.02858 (3.025) | 0.03337 (3.532) | 0.00073 (0.077) |
짧은 | 0.00065 (1.0) | 0.00044 (0.687) | 0.03511 (53.82) | 0.00049 (0.759) | 0.00163 (2.506) | 0.00234 (3.601) | 0.00005 (0.073) |
긴 | 0.00889 (1.0) | 0.00572 (0.643) | 0.36982 (41.56) | 0.00614 (0.690) | 0.02530 (2.844) | 0.02931 (3.294) | 0.00059 (0.066) |
미친 | 0.02918 (1.0) | 0.01991 (0.682) | 1.88695 (64.66) | 0.02003 (0.686) | 7.46894 (255.9) | 0.64994 (22.27) | 0.00327 (0.112) |
SQLGlot은 dateutil을 사용하여 리터럴 시간델타 표현식을 단순화합니다. 모듈을 찾을 수 없는 경우 최적화 프로그램은 다음과 같은 표현식을 단순화하지 않습니다.
x + interval ' 1 ' month