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

Pra*_*hed 5 python mysql recursion sqlalchemy common-table-expression

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

我实际上正在使用 Django 2.2 和 Python 3.7、Conda 和 SQLAlchemy 和 MySQL 8。但据我了解/已经注意到,这对我的问题并不重要。

我按照本指南在 MySQL 中创建了一个相邻列表模型,并查看了SQLAlchemy 的手册。 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)
);
Run Code Online (Sandbox Code Playgroud)

并使用这个虚拟条目:

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);
Run Code Online (Sandbox Code Playgroud)

在 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')
Run Code Online (Sandbox Code Playgroud)

到目前为止,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;
Run Code Online (Sandbox Code Playgroud)

我找到了这个查询示例并尝试按如下方式对其进行调整(其中 asparent应该代表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()
Run Code Online (Sandbox Code Playgroud)

上面代码的最后一行发出错误说明(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;
Run Code Online (Sandbox Code Playgroud)

问题

  1. 有谁可以提供一个链接,其中详细说明了如何在 SQLAlchemy 中转换为递归 CTE 查询?
  2. .cte(recursiv=True)SQLAlchemy 查询中到底做了什么?name=hierarchy可能是设置一个特定的名称,否则该名称将是虚构的。

  3. 为什么会发出错误Data too long...,我该如何解决这个问题(这个问题的实际输出并不重要,而是要找出到底什么太长以及它来自哪里)?

  4. 为什么需要引用例如\c字段?它从何而来?hh.c.nameh.c.level

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

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

对于如何改进我的问题的任何帮助或建议,我们将不胜感激。