【问题标题】:sqlalchemy does not execute oracle after insert trigger插入触发器后sqlalchemy不执行oracle
【发布时间】:2015-10-31 01:39:45
【问题描述】:

我有两个名为 movimiento (id_operacion, id_pago, importe) 和 ticket (id_movimiento, id_localidad, nro_ticket, precio, impreso, id_movimiento_anterior) 的表,并且 ticket 表有一个插入后触发器,它从名为 funcion 的表中分配 nro_ticket 值,该表保存了序列值。

这是插入前触发器:

create or replace TRIGGER "TICKETS4_TST"."TRG_B_I_TICKET" 
BEFORE INSERT ON TICKET REFERENCING NEW AS NEW
FOR EACH ROW
DECLARE
  ticket NUMBER;
BEGIN
  ticket := OBTENER_NUMERO_TICKET(SUBSTR(:new.id_localidad,0,9));
  :new.nro_ticket := ticket;
END;

以及获取nro_ticket值的函数:

create or replace FUNCTION obtener_numero_ticket (id_fun IN VARCHAR2)
RETURN NUMBER
IS
nro_ticket NUMBER;
BEGIN
  SELECT seq_ticket into nro_ticket FROM funcion where id_funcion = id_fun;
  RETURN (nro_ticket + 1);
END obtener_numero_ticket;

这是插入后触发器:

create or replace TRIGGER "TICKETS4_TST"."TRG_A_I_TICKET" 
AFTER INSERT ON TICKET REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
  UPDATE funcion SET seq_ticket = :new.nro_ticket WHERE id_funcion= SUBSTR(:new.id_localidad,0,9);
END;

这是 SQLAlchemy 代码:

sql_movimientos = """INSERT INTO movimiento (id_operacion, id_pago,
                                             importe)
                     VALUES ('%s', '%s', '%s')
                     RETURNING id_movimiento INTO :id_movimiento"""
sql_tickets = """INSERT INTO ticket (id_movimiento, id_localidad,
                                     precio, impreso,
                                     id_movimiento_anterior)
                 VALUES ('%s', '%s', '%s',
                         '%s', '%s')
                 RETURNING nro_ticket INTO :nro_ticket"""

with db.session as session:
    try:
        cursor = session.connection().connection.cursor()
        nuevo_id_movimiento = cursor.var(NUMBER)
        nuevo_nro_ticket = cursor.var(NUMBER)
        print sql_movimientos % (
            movimiento.operacion, movimiento.pago, movimiento.importe)
        session.execute(sql_movimientos % (
            movimiento.operacion, movimiento.pago, movimiento.importe),
             {'id_movimiento': nuevo_id_movimiento})
        nuevo_id_movimiento = int(nuevo_id_movimiento.getvalue())
    except Exception, e:
        session.rollback()
        raise e

    try:
        print sql_tickets % (nuevo_id_movimiento,
            ticket.localidad.id, ticket.precio, ticket.impreso,
            ticket.movimiento_anterior)
        session.execute(sql_tickets % (nuevo_id_movimiento,
            ticket.localidad.id, ticket.precio, ticket.impreso,
            ticket.movimiento_anterior),
            {'nro_ticket': nuevo_nro_ticket})
        nuevo_nro_ticket = int(nuevo_nro_ticket.getvalue())
        print nuevo_nro_ticket
    except Exception, e:
        session.rollback()
        raise e

这就是 sqlalchemy 翻译查询的方式:

INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO :id_movimiento
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES ('5725976', '------------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO :nro_ticket
17 << nro_ticket value
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO :id_movimiento
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES ('5725977', '-------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO :nro_ticket
17 << nro_ticket value
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO :id_movimiento
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES ('5725978', '----------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO :nro_ticket
17 << nro_ticket value
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO :id_movimiento
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES ('5725979', '--------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO :nro_ticket
17 << nro_ticket value
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO :id_movimiento
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES ('5725980', '--------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO :nro_ticket
17 << nro_ticket value

但如果在 SQLDeveloper 中作为匿名块执行相同的操作,我会得到正确的值:

declare
  id_movimiento number;
  nro_ticket number;
begin
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO id_movimiento;
DBMS_OUTPUT.put_line(id_movimiento);
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES (id_movimiento, '------------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO nro_ticket;
DBMS_OUTPUT.put_line(nro_ticket);
INSERT INTO movimiento (id_operacion, id_pago, importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO id_movimiento;
DBMS_OUTPUT.put_line(id_movimiento);
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES (id_movimiento, '--------------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO nro_ticket;
DBMS_OUTPUT.put_line(nro_ticket);
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO id_movimiento;
DBMS_OUTPUT.put_line(id_movimiento);
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES (id_movimiento, '---------------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO nro_ticket;
DBMS_OUTPUT.put_line(nro_ticket);
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO id_movimiento;
DBMS_OUTPUT.put_line(id_movimiento);
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES (id_movimiento, '--------------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO nro_ticket;
DBMS_OUTPUT.put_line(nro_ticket);
INSERT INTO movimiento (id_operacion, id_pago,
                                                     importe)
                             VALUES ('639086', '566365', '100')
                             RETURNING id_movimiento INTO id_movimiento;
DBMS_OUTPUT.put_line(id_movimiento);
INSERT INTO ticket (id_movimiento, id_localidad,
                                             precio, impreso,
                                             id_movimiento_anterior)
                         VALUES (id_movimiento, '---------------------', '100',
                                 '0', '')
                         RETURNING nro_ticket INTO nro_ticket;
DBMS_OUTPUT.put_line(nro_ticket);
end;

哪个输出这个:

5725981
17 << nro_ticket value
5725982
18 << nro_ticket value
5725983
19 << nro_ticket value
5725984
20 << nro_ticket value
5725985
21 << nro_ticket value

所以我认为问题是 SQLALchemy,但我不明白为什么它不起作用。

【问题讨论】:

  • 在您的第一个代码块中,您正在硬编码插入到票证表中的 id_movimiento 的值,在第二个代码块中您使用的是变量。在您的代码中,您显示了更新功能表的 seq_ticket 列的触发器,但是现在如何设置票表的 nro_ticket 列(它不是您的插入语句的一部分)。在 SQL Developer 中,您的代码全部在一个会话中执行,那么在 SQLAlchemy 中呢?您只显示了所涉及的 SQL 语句,而不是任何周围的代码。
  • @Sentinel 更新了它。我执行原始查询的原因是因为 session.add 不允许我使用 returning into,就像我之前的问题一样:stackoverflow.com/questions/31709252/…
  • 在 oracle 中触发器按以下顺序触发 1) 所有 Before 语句触发 2) 所有在每行之前触发 3) 所有在每行之后触发 4) 所有 after 语句触发。在您的情况下,您有 2 和 3。如果 SQL Alchemy 正在批处理似乎可能的更新,那么您的插入后触发器将在所有插入前触发器都被触发之后才会触发。结果,多行获得相同的 nro_ticket 值,然后 seq_ticket 值多次更新为相同的值。在 SQLDev 中,每个语句一次处理一个,因此触发器按预期触发。
  • @Sentinel 这正是发生的事情,不幸的是我能够解决这个问题的唯一方法(我真的不想称之为“修复”,更多的是一个“短期问题solver") 是每次我向票证交易添加值时提交,这并没有像一开始计划的那样完全工作。问题是会话本身不会执行触发器,除非它提交到数据库中。
  • Leandro,这样的情况就是数据库序列的用途。

标签: python oracle triggers sqlalchemy


【解决方案1】:

根据您发布的代码,对我来说,您不清楚您是如何创建 SQLAlchemy session 的。无论如何,因为对我来说你的代码看起来不错,我已经对 Oracle 数据库进行了试用,它可以工作。我就是这样做的,

通过SQLAlchemy创建了2个表(可能你直接做了)。

In [2]:

class Movimiento(Base):
    __tablename__ = 'movimiento'

    id_movimiento = Column(Integer, primary_key=True)
    id_operacion = Column(Integer)
    id_pago = Column(Integer)
    importe = Column(Integer)

In [3]:

class Ticket(Base):
    __tablename__ = 'ticket'

    nro_ticket = Column(Integer, primary_key=True)
    id_movimiento = Column(Integer)
    id_localidad = Column(Integer)
    precio = Column(Integer)
    impreso = Column(Integer)
    id_movimiento_anterior = Column(Integer)

直接创建序列,

create sequence movimiento_seq;
create sequence ticket_seq;

create or replace trigger movimiento_trigger
  before insert on movimiento
  for each row
  begin
    select movimiento_seq.nextval into :new.id_movimiento from dual;
  end;

create or replace trigger ticket_trigger
  before insert on ticket
  for each row
  begin
    select ticket_seq.nextval into :new.nro_ticket from dual;
  end;

然后是会话:

engine = create_engine('oracle://test:test@localhost:1521/orcl')

Base.metadata.create_all(engine)

Base.metadata.bind = engine

DBSession = sessionmaker(bind=engine)

session = DBSession()

把你的代码变成一个函数,

from cx_Oracle import NUMBER

sql_movimientos = """INSERT INTO movimiento (id_operacion, id_pago,
                                             importe)
                     VALUES ('%s', '%s', '%s')
                     RETURNING id_movimiento INTO :id_movimiento"""
sql_tickets = """INSERT INTO ticket (id_movimiento, id_localidad,
                                     precio, impreso,
                                     id_movimiento_anterior)
                 VALUES ('%s', '%s', '%s',
                         '%s', '%s')
                 RETURNING nro_ticket INTO :nro_ticket"""

#with session as session:
def ins_mov_tick(movimiento, ticket):
    try:
        cursor = session.connection().connection.cursor()
        nuevo_id_movimiento = cursor.var(NUMBER)
        nuevo_nro_ticket = cursor.var(NUMBER)
        print sql_movimientos % (
            movimiento.id_operacion, movimiento.id_pago, movimiento.importe)
        session.execute(sql_movimientos % (
            movimiento.id_operacion, movimiento.id_pago, movimiento.importe),
             {'id_movimiento': nuevo_id_movimiento})
        nuevo_id_movimiento = int(nuevo_id_movimiento.getvalue())
    except Exception, e:
        session.rollback()
        raise e

    try:
        print sql_tickets % (nuevo_id_movimiento,
            ticket.id_localidad, ticket.precio, ticket.impreso,
            ticket.id_movimiento_anterior)
        session.execute(sql_tickets % (nuevo_id_movimiento,
            ticket.id_localidad, ticket.precio, ticket.impreso,
            ticket.id_movimiento_anterior),
            {'nro_ticket': nuevo_nro_ticket})
        nuevo_nro_ticket = int(nuevo_nro_ticket.getvalue())
        print nuevo_nro_ticket
    except Exception, e:
        session.rollback()

(您还会看到一些小的修改,因为我使用的是上面创建的 MovimientoTicket 类,而您使用的是一些更复杂的类结构)。

然后每次我运行该函数时,我都会看到票号在增加:

In [21]:

ins_mov_tick(movimiento, ticket)
INSERT INTO movimiento (id_operacion, id_pago,
                                             importe)
                     VALUES ('1', '2', '45')
                     RETURNING id_movimiento INTO :id_movimiento
INSERT INTO ticket (id_movimiento, id_localidad,
                                     precio, impreso,
                                     id_movimiento_anterior)
                 VALUES ('3', '17', '45',
                         '1', '4')
                 RETURNING nro_ticket INTO :nro_ticket
3

In [23]:

ins_mov_tick(movimiento, ticket)
INSERT INTO movimiento (id_operacion, id_pago,
                                             importe)
                     VALUES ('1', '2', '45')
                     RETURNING id_movimiento INTO :id_movimiento
INSERT INTO ticket (id_movimiento, id_localidad,
                                     precio, impreso,
                                     id_movimiento_anterior)
                 VALUES ('4', '17', '45',
                         '1', '4')
                 RETURNING nro_ticket INTO :nro_ticket
4

我没有创建表 funcion 或触发器,因为我认为这对您的问题无关紧要。

无论如何,从这个冗长的解释中,我只是想说你的代码应该没问题,我发现它对你不起作用的唯一原因是你必须处理session(基本上,因为我不太了解你是如何做到的,所以我看不出它来自哪里)。另一种可能是我误解了一切。

希望对您有所帮助。

【讨论】:

  • 问题是 nro_ticket 不能是自动增量值,因为我必须能够更改序列值。这就是为什么我认为触发器没有执行。
  • 那么,在你的帖子的第二部分,当你说你已经在movimientoticket 中运行了这些插入时,我有点迷失了,你使用了哪些触发器?
  • 对不起,我忘记发布获取 nro_ticket 序列的前插入触发器。我马上更新。
  • 好的,我终于明白你要做什么了......我可以或多或少地快速测试它,因为我已经准备好了设置,但是,首先,我想问你,你能得到一张票的并发请求吗?我猜是这样,如果可以的话,你实现的机制不是很健壮,它可能会将相同的nro_ticket 分配给 2 个寄存器。
  • nro_ticket 是每个funcion 的连续数字列表真的很重要吗?你能有一个序列作为nro_ticket吗?然后,每当您想获得一张funcion 的门票时,您都可以轻松完成,您可以在 ddbb 中找到信息。我认为这会更健壮,您根本不必关心并发性,oracle 序列会处理它...否则,如果您真的需要连续的票号,我会将其作为单独的列进行,我会保留序列为 pk。但这些只是建议,如果你需要坚持你所拥有的,请告诉我
猜你喜欢
  • 1970-01-01
  • 2012-03-30
  • 2018-03-20
  • 2017-06-26
  • 1970-01-01
  • 1970-01-01
  • 2015-08-20
  • 2011-12-06
  • 2015-06-06
相关资源
最近更新 更多