SQLGlot は、依存関係のない SQL パーサー、トランスパイラー、オプティマイザー、およびエンジンです。 SQL のフォーマットや、DuckDB、Presto / Trino、Spark / Databricks、Snowflake、BigQuery などの 24 の異なる言語間の変換に使用できます。これは、対象の方言でさまざまな SQL 入出力を読み取り、構文的にも意味的にも正しい SQL を読み取ることを目的としています。
これは、堅牢なテスト スイートを備えた非常に包括的な汎用 SQL パーサーです。また、純粋に Python で書かれているにもかかわらず、非常にパフォーマンスが優れています。
パーサーのカスタマイズ、クエリの分析、式ツリーのトラバース、プログラムによる SQL の構築が簡単に行えます。
構文エラーが強調表示され、構成に応じて方言の非互換性が警告または表示される場合があります。ただし、SQLGlot は SQL バリデーターを目的としていないため、特定の構文エラーを検出できない可能性があります。
SQLGlot の詳細については、API ドキュメントと式ツリーの入門書をご覧ください。
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")
)。無効な 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 は、2 つの式間の意味上の違いを計算し、ソース式をターゲット式に変換するために必要な一連のアクションの形式で変更を出力できます。
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 Web サイトにあります。または、以下を使用してローカルに構築できます。
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ツリー | SQLパース | moz_sql_parser | スクロキシド |
---|---|---|---|---|---|---|---|
ちっぱい | 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 を使用してリテラルの timedelta 式を簡素化します。モジュールが見つからない場合、オプティマイザは次のような式を簡略化しません。
x + interval ' 1 ' month