Я хочу получить запрос с помощью SQLAlchemy для примера запроса, показанного на странице https://www.sqlite.org/lang_with.html:
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 компиляции времени для нижнего запроса в рекурсивном запросе, если он использует orderringring. Как убрать скобки из 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()
Нижняя часть SELECT без скобок (и без order_by) не имеет ошибки:
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