Pony ORM JOIN 语法
Pony ORM JOIN syntax
我已经开始使用 Pony,但还没有真正理解如何使用连接。在我看到 left_join() 与两个 for 子句一起使用的示例中,但是当我尝试在我的代码中重复它时,我得到类似 "collection was expected, got "for p in Pond"" 的错误
也许有人可以解释如何使用它或将我指向已经解释过的文档页面?
假设我们有以下实体:
from pony import orm
db = orm.Database()
class Person(db.Entity):
id = orm.PrimaryKey(int, auto=True)
name = orm.Required(str)
age = orm.Required(int)
contacts = orm.Set('Contact')
class Contact(db.Entity):
id = orm.PrimaryKey(int, auto=True)
person = orm.Required('Person')
type = orm.Required(str)
value = orm.Required(str)
db.generate_mapping(create_tables=True)
with orm.db_session:
john = Person(name='John', age=23)
mary = Person(name='Mary', age=21)
mike = Person(name='Mike', age=18)
arthur = Person(name='Arthur', age=25)
john.contacts.create(type='mobile', value='1234567')
john.contacts.create(type='email', value='john@example.com')
mary.contacts.create(type='mobile', value='76543321')
mary.contacts.create(type='skype', value='mary123')
mike.contacts.create(type='mobile', value='2345678')
现在我们要为每个 20 岁以上的人打印人名和联系信息。我们可以通过多种方式实现。
第一种方式是当我们明确声明连接条件时。这种方式非常冗长:
query = orm.select(
(p.name, c.value)
for p in Person for c in Contact
if p.age > 20 and c.person == p
)
query.show()
在此查询中,我们明确声明连接条件:c.person == p
。查询将向我们显示以下结果:
p.name|c.type|c.value
------+------+----------------
John |email |john@example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
如您所见,虽然 Arthur 的年龄大于 20 岁,但他并未包含在结果中。这是因为这种类型的联接是内部联接,结果仅包含可以在以下位置找到的人员至少一个联系人。
第二种加入方式是当我们遍历集合属性时:
query = orm.select(
(p.name, c.value)
for p in Person for c in p.contacts
if p.age > 20
)
query.show()
这种类型的连接最常用。这非常方便,因为我们不需要显式指定连接条件。查询结果与之前相同:
p.name|c.type|c.value
------+------+----------------
John |email |john@example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
出于与之前相同的原因,亚瑟仍未出现在列表中。如果我们想将 Arthur 包含在结果中,我们需要使用其他类型的连接,即左连接:
query = orm.left_join(
(p.name, c.value)
for p in Person for c in p.contacts
if p.age > 20
)
query.show()
在这种情况下,查询结果包括具有 None 值而不是 phone 数字的 Arthur:
p.name|c.type|c.value
------+------+----------------
Arthur|None |None
John |email |john@example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
当您使用 left_join
时,您需要循环收集。在这种情况下,Pony 将连接条件添加到 SQL 命令的 LEFT JOIN
子句的 ON
部分。
如果你使用 left_join
,你不能像在第一个查询中那样进行显式连接,因为在那种情况下 Pony 不知道要将哪个条件放入 [=22= 的 ON
部分] 条款。
有时手动指定 ON
部分的内容可能很有用。目前 Pony 不支持此类查询,但将来可能会添加此功能。
在许多情况下使用 PonyORM 时,可以在根本不进行连接的情况下检索数据。例如,您可以编写以下循环来打印人名和 phone 号码:
with db_session:
for p in Person.select(lambda p: p.age > 20):
print(p.name)
for c in p.contacts:
print(c.type, c.value)
在其他 ORM 中,这将导致 "N+1 query" 问题,其中每个人的联系人都由单独的 SQL 查询检索。 Pony 尝试自动优化查询以避免 "N+1 query" 模式。
在某些情况下,连接是隐式的。例如,要查找姓名以'M'开头的人的所有联系人,可以这样写:
query = select(c for c in Contact if c.person.name.startswith('M'))
for c in query:
print(c.person.name, c.type, c.value)
在这种情况下,Person table 是隐式连接的,只是因为您执行了从 Contact 到 Person 的属性遍历。
我已经开始使用 Pony,但还没有真正理解如何使用连接。在我看到 left_join() 与两个 for 子句一起使用的示例中,但是当我尝试在我的代码中重复它时,我得到类似 "collection was expected, got "for p in Pond"" 的错误 也许有人可以解释如何使用它或将我指向已经解释过的文档页面?
假设我们有以下实体:
from pony import orm
db = orm.Database()
class Person(db.Entity):
id = orm.PrimaryKey(int, auto=True)
name = orm.Required(str)
age = orm.Required(int)
contacts = orm.Set('Contact')
class Contact(db.Entity):
id = orm.PrimaryKey(int, auto=True)
person = orm.Required('Person')
type = orm.Required(str)
value = orm.Required(str)
db.generate_mapping(create_tables=True)
with orm.db_session:
john = Person(name='John', age=23)
mary = Person(name='Mary', age=21)
mike = Person(name='Mike', age=18)
arthur = Person(name='Arthur', age=25)
john.contacts.create(type='mobile', value='1234567')
john.contacts.create(type='email', value='john@example.com')
mary.contacts.create(type='mobile', value='76543321')
mary.contacts.create(type='skype', value='mary123')
mike.contacts.create(type='mobile', value='2345678')
现在我们要为每个 20 岁以上的人打印人名和联系信息。我们可以通过多种方式实现。
第一种方式是当我们明确声明连接条件时。这种方式非常冗长:
query = orm.select(
(p.name, c.value)
for p in Person for c in Contact
if p.age > 20 and c.person == p
)
query.show()
在此查询中,我们明确声明连接条件:c.person == p
。查询将向我们显示以下结果:
p.name|c.type|c.value
------+------+----------------
John |email |john@example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
如您所见,虽然 Arthur 的年龄大于 20 岁,但他并未包含在结果中。这是因为这种类型的联接是内部联接,结果仅包含可以在以下位置找到的人员至少一个联系人。
第二种加入方式是当我们遍历集合属性时:
query = orm.select(
(p.name, c.value)
for p in Person for c in p.contacts
if p.age > 20
)
query.show()
这种类型的连接最常用。这非常方便,因为我们不需要显式指定连接条件。查询结果与之前相同:
p.name|c.type|c.value
------+------+----------------
John |email |john@example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
出于与之前相同的原因,亚瑟仍未出现在列表中。如果我们想将 Arthur 包含在结果中,我们需要使用其他类型的连接,即左连接:
query = orm.left_join(
(p.name, c.value)
for p in Person for c in p.contacts
if p.age > 20
)
query.show()
在这种情况下,查询结果包括具有 None 值而不是 phone 数字的 Arthur:
p.name|c.type|c.value
------+------+----------------
Arthur|None |None
John |email |john@example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
当您使用 left_join
时,您需要循环收集。在这种情况下,Pony 将连接条件添加到 SQL 命令的 LEFT JOIN
子句的 ON
部分。
如果你使用 left_join
,你不能像在第一个查询中那样进行显式连接,因为在那种情况下 Pony 不知道要将哪个条件放入 [=22= 的 ON
部分] 条款。
有时手动指定 ON
部分的内容可能很有用。目前 Pony 不支持此类查询,但将来可能会添加此功能。
在许多情况下使用 PonyORM 时,可以在根本不进行连接的情况下检索数据。例如,您可以编写以下循环来打印人名和 phone 号码:
with db_session:
for p in Person.select(lambda p: p.age > 20):
print(p.name)
for c in p.contacts:
print(c.type, c.value)
在其他 ORM 中,这将导致 "N+1 query" 问题,其中每个人的联系人都由单独的 SQL 查询检索。 Pony 尝试自动优化查询以避免 "N+1 query" 模式。
在某些情况下,连接是隐式的。例如,要查找姓名以'M'开头的人的所有联系人,可以这样写:
query = select(c for c in Contact if c.person.name.startswith('M'))
for c in query:
print(c.person.name, c.type, c.value)
在这种情况下,Person table 是隐式连接的,只是因为您执行了从 Contact 到 Person 的属性遍历。