SQLAlchemy基本递归CTE示例-层次树查询

时间:2019-08-12 10:18:31

标签: python mysql recursion sqlalchemy common-table-expression

我无法理解如何在SQLAlchemy中实现递归CTE 。要么没有例子,要么我听不懂或者找不到他们。

我实际上正在将Django 2.2与Python 3.7,Conda和SQLAlchemy与MySQL 8结合使用。但是据我了解/已经注意到,这与我的问题无关紧要。

我已按照this指南在MySQL中创建了相邻列表模型,并查看了SQLAlchemy的this手册。 问题在MWE之后。

我的数据库定义为:

CREATE TABLE events (
 id int(10) UNSIGNED AUTO_INCREMENT,
 name varchar(255) ,
 parent_id int(10) UNSIGNED DEFAULT NULL,
 description varchar(255) DEFAULT=NULL,
 PRIMARY KEY (id),
 FOREIGN KEY (parent_id) REFERENCES events (id)
);

并使用此虚拟条目:

INSERT INTO events (id, name, parent_id) VALUES (1, "root", null);
INSERT INTO events (id, name, parent_id) VALUES (2, "A", 1);
INSERT INTO events (id, name, parent_id) VALUES (3, "B", 1);
INSERT INTO events (id, name, parent_id) VALUES (4, "C", 1);
INSERT INTO events (id, name, parent_id) VALUES (5, "D", 1);
INSERT INTO events (id, name, parent_id) VALUES (6, "A1", 2);
INSERT INTO events (id, name, parent_id) VALUES (7, "A2", 2);
INSERT INTO events (id, name, parent_id) VALUES (8, "B1", 3);
INSERT INTO events (id, name, parent_id) VALUES (9, "B2", 3);
INSERT INTO events (id, name, parent_id) VALUES (10, "D1", 5);
INSERT INTO events (id, name, parent_id) VALUES (11, "A1A", 6);
INSERT INTO events (id, name, parent_id) VALUES (12, "A1B", 6);

在Django中,我创建了此模型定义以通过SQLAlchemy访问

class Events(Base):
    __tablename__ = 'events'
    id = Column(INTEGER(10), primary_key=True)
    parent_id = Column(INTEGER(10), ForeignKey('events.id'))
    name = Column(String(255))
    description = Column(String(255))

    children = relationship('Events')

到目前为止,SQLAlchemy中的简单查询就像一个魅力。但是我不明白如何将WITH RECURSIVE和类似的请求转换为SQLAlchemy查询。

例如,要求5列(最后2列通过递归更改/创建)显示每个数据库条目的树和深度:

WITH RECURSIVE hierarchy AS
(
  SELECT id, name, parent_id, 1 AS depth, name AS path
    FROM events
    WHERE parent_id IS NULL
  UNION ALL
  SELECT e.id, e.name, e.parent_id, h.depth + 1, CONCAT(h.path, ' > ', e.name)
    FROM hierarchy AS h 
      JOIN events AS e ON h.id = e.parent_id
)
SELECT * FROM hierarchy;

我找到了this个查询示例,并尝试如下进行调整(其中parent应该代表上面的path

hierarchy = self.session.query(
        Events, literal(0).label('level'), null().label('parent')
    ).filter(
        Events.parent_id == null()
    ).cte(name='hierarchy',recursive=True)

h = aliased(hierarchy, 'h')
e = aliased(Events, 'e')

hierarchy = hierarchy.union(
    self.session.query(
        e2, (h.c.level + 1).label('level'), h.c.name                         
    ).filter(
        (e2.parent_id == h.c.id)
    )
)
result = self.session.query(Events, hierarchy.c.level).all()

以上代码的最后一行正在发出错误,指出(1406, "Data too long for column 'parent' at row 1")以及创建的SQL语句:

WITH RECURSIVE hierarchy(id, parent_id, name, description, level, parent) AS (
 SELECT events.id AS id, events.parent_id AS parent_id, events.name AS name, events.description AS description, %(param_1)s AS level, %(param_2)s AS parent 
 FROM events 
 WHERE events.parent_id IS NULL 
 UNION 
 SELECT e2.id AS e2_id, e2.parent_id AS e2_parent_id, e2.name AS e2_name, e2.description AS e2_description, anon_1.level + %(level_1)s AS level, anon_1.name AS anon_1_name 
 FROM events AS e2, hierarchy AS anon_1 
 WHERE e2.parent_id = anon_1.id
)
SELECT events.id AS events_id, events.parent_id AS events_parent_id, events.name AS events_name, events.description AS events_description, hierarchy.level AS hierarchy_level 
FROM events, hierarchy;

问题

  1. 任何人都可以提供包含详细说明的链接吗? 在SQLAlchemy中如何转换为递归CTE查询?
  2. SQLAlchemy查询中的.cte(recursiv=True)到底是什么? name=hierarchy可能正在设置一个特定的名称,否则它将由某些内容组成。

  3. 为什么会发出错误Data too long...,我又该如何解决(这个问题的实际输出并不重要,而是找出太长的确切时间以及它的来源)?< / p>

  4. 为什么要引用c作为h的字段,例如h.c.name \ h.c.level是必需的,它从哪里来?

  5. (如何在此查询中使用func.concat来显示类似于CONCAT(h.path, ' > ', e.name)的路径?)

我可以看到这是一个不寻常的问题,我可能从一个错误的角度看待它,但是我花了很多时间来理解这个主题,并且还没有走得很远。

感谢您提供任何有关改善我的问题的帮助或建议。

0 个答案:

没有答案