我想在页面https://www.sqlite.org/lang_with.html上显示的示例查询中使用SQLAlchemy进行查询:
WITH RECURSIVE under_alice(name,level) AS (
VALUES('Alice',0)
UNION ALL
SELECT org.name, under_alice.level+1
FROM org JOIN under_alice ON org.boss=under_alice.name
ORDER BY 2 DESC
)
SELECT substr('..........',1,level*3) || name FROM under_alice;
我正在尝试执行一个自我递归查询,该查询属于SQLite的语法错误:
OperationalError:“(”:语法错误附近的(sqlite3.OperationalError)
如果SQLAlchemy在递归查询中为底部查询的时间编译SQL语句加括号时引发异常,如果它使用排序环。如何从SQL中删除括号?
再现异常:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
parent_id = db.Column(db.Integer, nullable=True)
name = db.Column(db.String(100), nullable=False)
def __repr__(self):
return f'<Category {self.name}>'
import logging
logging.basicConfig()
logger = logging.getLogger('sqlalchemy.engine')
logger.setLevel(logging.INFO)
db.create_all()
类别数据:
data = [
{'parent_id': None, 'name': 'Everything for rehabilitation'},
{'parent_id': 1, 'name': 'Wheelchairs'},
{'parent_id': 1, 'name': 'Wheelchairs 2'},
{'parent_id': 1, 'name': 'Scooters for the disabled'},
{'parent_id': 2, 'name': 'Active wheelchairs'},
{'parent_id': 2, 'name': 'Children wheelchairs'},
{'parent_id': 2, 'name': 'Children wheelchairs 2'},
{'parent_id': 2, 'name': 'Wheelchair Otto Bock'},
{'parent_id': 2, 'name': 'Wheelchair Vermeiren'},
{'parent_id': 3, 'name': 'Folding wheelchairs'},
{'parent_id': 3, 'name': 'Wheelchair for disabled people'},
{'parent_id': 3, 'name': 'Wheelchairs for transportation of patients'},
{'parent_id': 4, 'name': 'Foldable scooters for disabled people'},
{'parent_id': 4, 'name': 'Three-wheeled scooters for disabled people'}
]
for param in data:
db.session.add(Category(**param))
db.session.commit()
查询:
top_query = db.session.query(Category, db.literal(0).label('level')) \
.filter(Category.parent_id == None) \
.cte(name='top_query', recursive=True)
top_query = db.aliased(top_query, name='my_category')
bottom_query = db.session.query(Category, (top_query.c.level + 1).label('level')) \
.join(top_query, Category.parent_id == top_query.c.id) \
.order_by(db.desc(Category.parent_id)) # !!!!!!!!!!!!!!!!!!!!!!
hierarchy_query = top_query.union_all(bottom_query)
db.session.query(hierarchy_query).all()
带括号的底部SELECT错误:
WITH RECURSIVE my_category(id, parent_id, name, level) AS (
SELECT category.id AS id,
category.parent_id AS parent_id,
category.name AS name,
? AS level
FROM category
WHERE category.parent_id IS NULL
UNION ALL
(SELECT category.id AS category_id,
category.parent_id AS category_parent_id,
category.name AS category_name,
my_category.level + ? AS level
FROM category JOIN my_category ON category.parent_id = my_category.id
ORDER BY category.parent_id DESC)
)
SELECT my_category.id AS my_category_id,
my_category.parent_id AS my_category_parent_id,
my_category.name AS my_category_name,
my_category.level AS my_category_level
FROM my_category
如果对order_by
进行了注释,则不会引发异常:
top_query = db.session.query(Category, db.literal(0).label('level')) \
.filter(Category.parent_id == None) \
.cte(name='top_query', recursive=True)
top_query = db.aliased(top_query, name='my_category')
bottom_query = db.session.query(Category, (top_query.c.level + 1).label('level')) \
.join(top_query, Category.parent_id == top_query.c.id) \
# .order_by(db.desc(Category.parent_id))
hierarchy_query = top_query.union_all(bottom_query)
db.session.query(hierarchy_query).all()
没有括号(也没有order_by)的底部SELECT没有错误:
WITH RECURSIVE my_category(id, parent_id, name, level) AS (
SELECT category.id AS id,
category.parent_id AS parent_id,
category.name AS name,
? AS level
FROM category
WHERE category.parent_id IS NULL
UNION ALL
SELECT category.id AS h_category_id,
category.parent_id AS category_parent_id,
category.name AS category_name,
my_category.level + ? AS level
FROM category JOIN my_category ON category.parent_id = my_category.id
)
SELECT my_category.id AS my_category_id,
my_category.parent_id AS my_category_parent_id,
my_category.name AS my_category_name,
my_category.level AS my_category_level
FROM my_category
答案 0 :(得分:0)
SQLAlchemy之所以以这种方式编译查询,是因为
SELECT x FROM foo UNION ALL (SELECT x FROM bar ORDER BY x)
和
SELECT x FROM foo UNION ALL SELECT x FROM bar ORDER BY x;
是不同的查询(在支持第一查询的语法的数据库中,例如Postgresql):
SQLite不支持以前查询中使用的语法,因此语法错误。
在WITH statement的SQLite实现中,将ORDER BY应用于整体选择的化合物具有以下含义:
如果存在ORDER BY子句,则它将确定步骤2a中从队列中提取行的顺序。 ...
因此,与其像对联盟中使用的第二个查询那样应用排序,不如将它应用于CTE中使用的复合选择,但是不幸的是,SQLAlchemy CTE
似乎不支持开箱即用。您可以为此实现自己的功能:
from sqlalchemy.sql.selectable import CTE
# Following the implementation of CTE.union_all():
def cte_order_by(self, *clauses):
return CTE(
self.original.order_by(*clauses),
name=self.name,
recursive=self.recursive,
_restates=self._restates.union([self]),
_suffixes=self._suffixes
)
,然后在查询中使用它:
hierarchy_query = top_query.union_all(bottom_query)
hierarchy_query = cte_order_by(hierarchy_query, db.desc('2'))