8

Digamos que tengo un modelo llamado Transaction que tiene un atributo :transaction_code. Quiero que ese atributo se complete automáticamente con un número de secuencia que puede diferir de id (por ejemplo, Transacción con id=1 podría tener transaction_code=1000).Ruby on Rails + PostgreSQL: uso de secuencias personalizadas

He intentado crear una secuencia en postgres y luego hacer que el valor predeterminado para la columna transaction_code sea el nextval de esa secuencia. La cosa es, si no se asigna ningún valor a @transaction.transaction_code en RoR, cuando emito un @transaction.save en RoR, se trata de hacer el siguiente SQL:

INSERT INTO transactions (transaction_code) VALUES (NULL);

Lo que esto hace es crear una nueva fila en la tabla Transacciones, con transaction_code como NULL, en lugar de calcular la nextval de la secuencia e insertarla en la columna correspondiente. Por lo tanto, como descubrí, si especifica NULL para postgres, supone que realmente desea insertar NULL en esa columna, independientemente de que tenga un valor predeterminado (vengo de ORACLE, que tiene un comportamiento diferente).

Estoy abierto a cualquier solución en esto, ya sea si se hace sobre la base de datos o en RoR:

  • o bien no es una forma de excluir atributos de ActiveRecord save
  • o hay una manera de cambiar el valor de una columna antes de inserto con un disparador
  • o si hay una manera de generar estos números de secuencia dentro de RoR
  • o de cualquier otra manera, siempre y cuando funciona :-)

Gracias de antemano.

Respuesta

2

Si desea insertar el valor predeterminado a una columna en una declaración INSERT, puede utilizar la palabra clave DEFAULT - sin comillas:

INSERT INTO mytable (col1, col2) VALUES (105, DEFAULT); 

O bien, podría explicar el valor por defecto, nextval(...) en Tu caso. Vea el manual here.


Un gatillo para ese caso es simple. Eso es realmente lo que recomendaría si quiere asegurarse de que solo se ingresen los números de su secuencia, sin importar qué.

CREATE OR REPLACE FUNCTION trg_myseq() 
    RETURNS trigger AS 
$BODY$ 
BEGIN 

NEW.mycol := nextval('my_seq'); 
RETURN NEW; 

END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE; 

CREATE TRIGGER myseq 
    BEFORE INSERT 
    ON mytable 
    FOR EACH ROW 
    EXECUTE PROCEDURE trg_myseq(); 

En una nota: Si desea asignar sus propios (no secuencial) con números 'secuencia', he escrito una solución para que, en una respuesta hace un par de días:
How to specify list of values for a postgresql sequence

+1

Gracias Erwin por su respuesta. El problema es que, dentro del método de guardado de ActiveRecord, no se puede "enviar" la palabra clave DEFAULT dentro de un INSERT (que resolvería el caso, sí). Preferiría una solución que podría estar codificada al 100% en la aplicación Ruby on Rails, en lugar de programarla en la base de datos, lo que me inhabilitaría para migrar sin problemas a otro DBMS si fuera necesario. –

+1

@Ricardo: "migrar sin problemas a otro DBMS" es una bonita fantasía, pero no tiene mucho que ver con la realidad, lo siento. Una gran desventaja de un desencadenante es que no podrás usarlo en una base de datos compartida de Heroku. –

6

Por el momento, que puede ser atrapado ir a buscar y asignar la secuencia en su modelo ROR así:

before_create :set_transaction_code_sequence 

def set_transaction_code_sequence 
    self.transaction_code = self.class.connection.select_value("SELECT nextval('transaction_code_seq')") 
end 

no estoy particularily aficionado a esta solución, ya que me gustaría ver este corr en AR directamente ... pero funciona.