python - 懒惰=“动态”有什么问题?有哪些替代方案?
问题描述
这个问题已经完全重写了 10/17/18
为了有一个“编辑版本控制系统”(类似于 StackOverflow 的功能),我配置了以下类:
tags = db.Table(
"tags",
db.Column("tag_id", db.Integer, db.ForeignKey("tag.id")),
db.Column("post_version_id", db.Integer,
db.ForeignKey("post_version.id"))
)
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
tag = db.Column(db.String(128), index=True, unique=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
head_id = db.Column(db.Integer, db.ForeignKey("post_version.id"))
class PostVersion(db.Model):
id = db.Column(db.Integer, primary_key=True)
previous_id = db.Column(db.Integer, db.ForeignKey("post_version.id"), default=None)
pointer_id = db.Column(db.Integer, db.ForeignKey("annotation.id"))
current = db.Column(db.Boolean, index=True)
tags = db.relationship("Tag", secondary=tags)
排除了帖子内容等不相关的列。实际上,真正的数据模型是注释;为了通用性,我已经简化了这些模型
实际数据由 136 个Post
可变标记和通过编辑可变版本组成;也就是说:我生成了 136Post
的。我有 15Tag
的。最初的 136Post
都与 2 一致地标记Tag
。我随后用不同的标签对 's 进行了可变标记(使用我的编辑系统;因此已编辑的 'sPost
有多个' s)。PostVersion
Post
您可能会注意到,在 Post 和 PostVersion 之间有一个循环引用;我使用它来配置以下两个关系以进行实验:
关系 1posts
posts = db.relationship("Post",
secondary="join(tags, PostVersion,"
"tags.c.post_version_id==PostVersion.id)",
primaryjoin="Tag.id==tags.c.tag_id",
secondaryjoin="Post.head_id==PostVersion.id",
lazy="dynamic")
它基于 SQL 语句
SELECT
post.id
FROM
tag
JOIN
tags ON tag.id=tags.tag_id
JOIN
post_version ON tags.post_version_id=post_version.id
JOIN
post ON post.head_id=post_version.id
WHERE
tag.id=<tag_id>
和
关系 2posts2
posts2 = db.relationship("Post",
secondary="join(tags, PostVersion,"
"and_(tags.c.post_version_id==PostVersion.id,"
"AnnotationVersion.current==True))",
primaryjoin="Tag.id==tags.c.tag_id",
secondaryjoin="PostVersion.pointer_id==Post.id",
lazy="dynamic")
基于SQL语句
SELECT
annotation.id
FROM
tag
JOIN
tags ON tag.id=tags.tag_id
JOIN
annotation_version ON tags.annotation_version_id=annotation_version.id AND
annotation_version.current=1
JOIN
annotation ON annotation_version.pointer_id = annotation.id
WHERE
tag_id=8;
这会产生以下数据:
Tag Actual len(t.posts.all()) len(t.posts.paginate(1,5,False).items)
t1 0 0 0
t2 1 136 5
t3 1 136 5
t8 136 136 1
t14 136 136 1
t15 24 136 1
Tag Actual t.posts.count() t.posts2.count()
t1 0 0 0
t2 1 136 163
t3 1 136 163
t8 136 22168 26569
t14 136 22168 26569
t15 24 3264 3912
我已经排除了冗余标签(即,所有其他Tag
带有 0Post
的标签)和相同的数据(即,结果posts2
与 for 相同posts
)。
如您所见,结果存在一些严重问题!特别是对于这两种关系,如果lazy="dynamic"
关闭,Post
则始终返回正确的。
在创建引擎时,@ IljaEveriläecho=True
发现lazy="dynamic"
更改了 SQL。我引用这个问题的评论:
简而言之:有
lazy="dynamic"
你得到FROM post, tags, post_version WHERE ...
,但没有你得到FROM post, tags JOIN post_version ON tags.post_version_id = post_version.id WHERE ....
正如你所见,你的复合二级几乎被动态设置忽略了。现在的问题是“为什么?”
我的问题:
1.这是一个错误吗?
2. 我能做些什么来改变这个困境?
更新:
这里似乎lazy="dynamic"
明确不鼓励,但没有建议替代方案。仍然允许分页并依靠大型集合的替代方法是什么?默认情况下不允许这样做(或者至少在我访问它的方式上)并且文档似乎没有澄清问题!在标题为使用哪种加载方式的部分中?它似乎为大型集合推荐的加载策略是lazy="subquery"
,但这不允许paginate()
and count()
。
解决方案
这确实是 SQLAlchemy 如何处理形成动态加载关系的查询的一个问题。虽然查询应该是
SELECT post.id AS post_id, post.head_id AS post_head_id
FROM post, tags JOIN post_version ON tags.post_version_id = post_version.id
WHERE ? = tags.tag_id AND post.head_id = post_version.id
它最终成为
SELECT post.id AS post_id, post.head_id AS post_head_id
FROM post, tags, post_version
WHERE ? = tags.tag_id AND post.head_id = post_version.id
post
因此,虽然and之间存在内部连接post_version
(在SQL-92之前的样式中),但缺少 and 之间的内部连接,因此tags
在与其余部分之间存在CROSS JOIN 。结果是查询加载了所有当前的帖子版本,而不管 tag(s)是什么,因为每个帖子都与. 这也解释了 的乘法。post_version
tags
tags
t.posts.count()
解决方案是等待修复,同时使用其他一些关系加载策略。
推荐阅读
- regex - PL/SQL 中的日期格式替换。示例:从 5y 6m 20d 到 050620
- android - Android自定义浏览器每次打开两次活动
- docker - 如果安装了整个主机文件系统,则 docker cp 返回“设备上没有剩余空间”
- c# - 使用 HTML 敏捷包提取 img src
- python - Python .replace() 函数,以某种方式删除反斜杠
- jquery - 如何将多个js文件放在一起
- javascript - innerHTML 和 document.createTextNode 有什么区别
- android - 抽屉图标没有响应点击
- java - 使用依赖的一小部分有意义吗?
- mysql - 如何制作与 4 个不同表相关的数据库表