在不设置外键的情况下如何进行多表查询呢


#1
# 用户表
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(40), unique=True)  
    password = db.Column(db.String(128)) 

# 订单表
class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    buy_id = db.Column(db.Integer) # 卖家ID  
    sell_id = db.Column(db.Integer) # 卖家ID
    status = db.Column(db.Integer) #状态

# 商品
class Sku(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    sku_name = db.Column(db.String(40))  
    money = db.Column(db.Float(20))

描述:一个用户可以是卖家也可以是买家。

这里是三张表,没有进行外键关联,需要按照订单状态进行查询(例如:0:已关闭,1:等待付款,3:等待发货),同时需要在模板中显示该笔订单所对应的卖家名称以及商品名称及价格及状态


#2

该如何进行查询并且渲染呢?


#3

我思考了一下,设置外键可能更加麻烦,后期不好扩展。以后的业务模块可能需要更多


#4

老样子,可以使用relationship进行关联。最关键的是使用relationshipprimaryjoin参数。

核心代码如下,之后就可以使用order1.buy_userorder1.sell_user访问订单买家和卖家了。

class Order(Model):
    """订单表"""
    __tablename__ = 'order'
    id = Column(Integer, primary_key=True)
    buy_id = Column(Integer)  # 卖家ID
    sell_id = Column(Integer)  # 卖家ID
    status = Column(Integer)  # 状态

    #######################################################
    # 不使用ForeignKey方式进行一对多关联
    #######################################################
    buy_user = relationship(
        "User",
        uselist=False,
        primaryjoin=foreign(buy_id) == remote(User.id)
    )
    sell_user = relationship(
        "User",
        uselist=False,
        primaryjoin=foreign(sell_id) == remote(User.id)
    )

完整代码:

# coding=utf-8

from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker


from sqlalchemy.orm import relationship
from sqlalchemy.orm import foreign, remote


DB_URI = 'sqlite:///:memory:'
engine = create_engine(DB_URI)
Model = declarative_base(engine)
Session = sessionmaker(engine)
session = Session()


class User(Model):
    """用户表"""
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    username = Column(String(40), unique=True)
    password = Column(String(128))


class Order(Model):
    """订单表"""
    __tablename__ = 'order'
    id = Column(Integer, primary_key=True)
    buy_id = Column(Integer)  # 卖家ID
    sell_id = Column(Integer)  # 卖家ID
    status = Column(Integer)  # 状态

    #######################################################
    # 不使用ForeignKey方式进行一对多关联
    #######################################################
    buy_user = relationship(
        "User",
        uselist=False,
        primaryjoin=foreign(buy_id) == remote(User.id)
    )
    sell_user = relationship(
        "User",
        uselist=False,
        primaryjoin=foreign(sell_id) == remote(User.id)
    )


class Sku(Model):
    """商品"""
    __tablename__ = 'sku'
    id = Column(Integer, primary_key=True)
    sku_name = Column(String(40))
    money = Column(Float(20))


Model.metadata.drop_all()
Model.metadata.create_all()

user1 = User(username='user1', password='password')
user2 = User(username='user2', password='password')
session.add(user1)
session.add(user2)
session.commit()

print('user1.id:', user1.id)    # user1.id: 1
print('user2.id:', user2.id)    # user2.id: 2


order1 = Order(buy_id=user1.id, sell_id=user2.id)
session.add(order1)
session.commit()

buy_user = order1.buy_user
if buy_user:
    print('buy_user:', buy_user.username)   # buy_user: user1

sell_user = order1.sell_user
if sell_user:
    print('sell_user:', sell_user.username)  # sell_user: user2


#5

外键是关系型数据库里表与表之间关系的体现,不设外键说明这些表之间没有关系,或者不用关系型数据库?


#6

在数据库层上,使用外键确实可以表现出表与表之间的关系,也能保证数据库数据的完整性和一致性。

不过有一种做法是不在数据库里定义外键,而是在应用程序层面来保证数据完整性即可,数据库层不做强制要求。这种做法有利有弊,看场景。


#7

可否来个栗子:thinking:


#8

感谢大佬,我先研究一下:grin:


#9

主要是表比较多,设置外键的话,User表就会变得很复杂,也不利于扩展。


#10

请问可不可以举个例子看看,小白没接触过,或者去搜索的关键字告诉我也行


#11

数据库层这一块我并不是专家,「要不要设置外键」也只是听过别人(DBA)的建议最好不设置。我现在还没碰到那个场景也就没管那么多,能用就行。如果你真有兴趣的话,可去看有关数据库的书籍。

外键对并发性能的影响比较大, 因为每次修改数据都需要去另外一个表检查数据, 需要获取额外的锁(以确保事务完成之前, 父表的记录不会被删除) , 高并发的环境下很容易出现性能问题。
——《MySQL DBA修炼之道》 => 第4章 开发进阶 => 4.9.3 存储过程、触发器、外键


#12

好的,谢谢