我在我的应用程序中发现了这个问题,使用Python3和SQLAlchemy连接的sqlite-database。我从面向对象的开发人员的角度设计了数据结构。我认为这是我的问题之一。 ;)
简单描述:
实体a
(来自表/类A
)可以具有对实体b
的多重引用(来自表/类B
)。
样品:
CREATE TABLE "B" (
oid INTEGER NOT NULL,
val INTEGER,
PRIMARY KEY (oid)
);
CREATE TABLE "A" (
oid INTEGER NOT NULL,
PRIMARY KEY (oid)
);
CREATE TABLE a_b_relation (
a_oid INTEGER,
b_oid INTEGER,
FOREIGN KEY(a_oid) REFERENCES "A" (oid),
FOREIGN KEY(b_oid) REFERENCES "B" (oid)
sqlite> SELECT oid FROM A;
1
sqlite> SELECT oid, val FROM B;
1|0
2|1
sqlite> SELECT a_oid, b_oid FROM a_b_relation;
1|1
1|1
您在此处看到a
有两个对同一实体/行b
的引用。
在我的数据结构中,这是有道理的。但也许它打破了SQL- / RDBMS规则?
当我在Python3中使用该对象执行此操作时会导致错误,因为SQLAlchemy尝试为此执行DELETE语句。
a._bbb.clear()
错误
DELETE FROM a_b_relation WHERE a_b_relation.a_oid = ? AND a_b_relation.b_oid = ?
(1, 1)
ROLLBACK
sqlalchemy.orm.exc.StaleDataError: DELETE statement on table 'a_b_relation' expected to delete 1 row(s); Only 2 were matched.
在这种情况下,错误对我有意义。但我不知道如何处理。
从我看来,它看起来像SQLAlchemy中的“bug”,因为构造的DELETE语句不会处理我的用例。但我当然知道,特别是SQLA开发人员会考虑他们做了什么,并且这种行为有一个很好的设计理由。
以下是创建和填充示例数据库以显示此问题的代码:
#!/usr/bin/env python3
import os.path
import os
import sqlalchemy as sa
import sqlalchemy.orm as sao
import sqlalchemy.ext.declarative as sad
from sqlalchemy_utils import create_database
_Base = sad.declarative_base()
session = None
a_b_relation= sa.Table('a_b_relation', _Base.metadata,
sa.Column('a_oid', sa.Integer, sa.ForeignKey('A.oid')),
sa.Column('b_oid', sa.Integer, sa.ForeignKey('B.oid'))
)
class A(_Base):
__tablename__ = 'A'
_oid = sa.Column('oid', sa.Integer, primary_key=True)
_bbb = sao.relationship('B', secondary=a_b_relation)
def __str__(self):
s = '{}.{} oid={}'.format(type(self), id(self), self._oid)
for b in self._bbb:
s += '\n\t{}'.format(b)
return s
class B(_Base):
__tablename__ = 'B'
_oid = sa.Column('oid', sa.Integer, primary_key=True)
_val = sa.Column('val', sa.Integer)
def __str__(self):
return '{}.{} oid={} val={}'.format(type(self), id(self), self._oid, self._val)
dbfile = 'set.db'
def _create_database():
if os.path.exists(dbfile):
os.remove(dbfile)
engine = sa.create_engine('sqlite:///{}'.format(dbfile), echo=True)
create_database(engine.url)
_Base.metadata.create_all(engine)
return sao.sessionmaker(bind=engine)()
def _fill_database():
a = A()
session.add(a)
for v in range(2):
b = B()
b._val = v
session.add(b)
if v == 0:
# THIS CAUSE THE PROBLEM I THINK
a._bbb += [b]
a._bbb += [b]
session.commit()
if __name__ == '__main__':
session = _create_database()
_fill_database()
a = session.query(A).first()
a._bbb.clear()
session.commit()
也许它有点远,但我也会描述原始的数据结构。它是关于为健身房制作使用统计数据。 :)
你走到一台机器上并且几次抬起几公斤 - 这被称为TrainingUnit
(在示例中相当于A
)。
为了预热,你需要重复12次,每次重复35公斤 - 在示例中称为SetSet
(相当于B
;请记住Set
是保留的SQL关键字)。
然后你做一分钟休息,用40公斤重复12次 - 第二组。然后再次进行第三次(!),重复12次,再重40次。
因此TrainingUnit
实例/行需要三个与SetSet
的实例/行的关系。因为第二组和第三组具有相同的值/设置,所以我在这里使用相同的SetSet
实例/行。
大多数情况下,运动员每天都使用相同的配置进行TrainingUnits - 这意味着每天都有相同的值。这就是我想重用SetSet
的实例/行的原因。
每个TrainingUnit的套数不固定 - 这取决于运动员他/她将制作多少套。
答案 0 :(得分:0)
我的解决方案取决于资源。谢谢你的帮助!
我使用示例代码来描述解决方案。
SQL中的架构......
class Route
{
public:
Route ( const Leg& myLeg ): theLeg( myLeg ) {}
private:
const Leg& theLeg;
};
...和SQLAlchemy
CREATE TABLE "A" (
oid INTEGER NOT NULL,
PRIMARY KEY (oid)
);
CREATE TABLE "B_Val" (
oid INTEGER NOT NULL,
val INTEGER,
PRIMARY KEY (oid)
);
CREATE TABLE "B" (
oid INTEGER NOT NULL,
b_val_fk INTEGER,
PRIMARY KEY (oid),
FOREIGN KEY(b_val_fk) REFERENCES "B_Val" (oid)
);
CREATE TABLE a_b_relation (
a_oid INTEGER,
b_oid INTEGER,
FOREIGN KEY(a_oid) REFERENCES "A" (oid),
FOREIGN KEY(b_oid) REFERENCES "B" (oid)
);