8

Dire che ho un modello chiamato Transaction che ha un attributo :transaction_code. Desidero che questo attributo venga automaticamente riempito con un numero di sequenza che potrebbe essere diverso da id (ad esempio, la transazione con id=1 potrebbe avere transaction_code=1000).Ruby on Rails + PostgreSQL: utilizzo di sequenze personalizzate

Ho cercato di creare una sequenza su postgres e quindi di creare il valore predefinito per la colonna transaction_code del nextval di quella sequenza. Il fatto è che, se non si assegna alcun valore per @transaction.transaction_code su RoR, quando lancio una @transaction.save su RoR, si cerca di fare il seguente SQL:

INSERT INTO transactions (transaction_code) VALUES (NULL);

Quello che fa è creare una nuova riga nella tabella Transactions, con transaction_code come NULL, invece di calcolare l'nextval della sequenza e inserirla nella colonna corrispondente. Quindi, come ho scoperto, se si specifica NULL a postgres, si presuppone che si voglia veramente inserire NULL in quella colonna, indipendentemente dal fatto che abbia un valore predefinito (vengo da ORACLE che ha un comportamento diverso).

Sono aperto a qualsiasi soluzione su questo, o se è fatto sulla base di dati o RoR:

  • o c'è un modo per escludere attributi di ActiveRecord save
  • o non c'è un modo per cambiare il valore di una colonna prima inserto con un trigger
  • o c'è un modo per generare questi numeri di sequenza all'interno di RoR
  • o qualsiasi altro modo, fintanto che funziona :-)

Grazie in anticipo.

risposta

2

Se si vuole inserire il valore predefinito a una colonna in una INSERT dichiarazione, è possibile utilizzare la parola chiave DEFAULT - senza virgolette:

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

Oppure si potrebbe precisare il default, nextval(...) in il tuo caso. Vedi lo manual here.


Un innesco per questo caso è semplice. Questo è in realtà ciò che raccomanderei se si desidera assicurarsi che vengano inseriti solo i numeri della sequenza, a prescindere da cosa.

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(); 

Su un lato nota: Se si desidera assegnare solo (non sequenziale) i numeri come 'sequenza', ho scritto una soluzione per questo in una risposta di un paio di giorni fa:
How to specify list of values for a postgresql sequence

+1

Grazie Erwin per la risposta. Il fatto è che, all'interno del metodo di salvataggio di ActiveRecord, non è possibile "inviare" la parola chiave DEFAULT all'interno di un INSERT (che risolverebbe il caso, sì). Preferirei una soluzione che potesse essere codificata al 100% all'interno dell'applicazione Ruby on Rails, invece di programmarla sul database, che mi impedirebbe di migrare senza problemi a un altro DBMS se necessario. –

+1

@Ricardo: "migrare senza problemi ad un altro DBMS" è una bella fantasia ma non ha molto a che fare con la realtà, mi dispiace. Un grande svantaggio di un trigger è che non sarai in grado di usarlo su un database condiviso Heroku. –

6

per il momento, si potrebbe essere bloccato il recupero e l'assegnazione della sequenza nel modello ROR in questo modo:

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 

io non sono particularily affezionato a questa soluzione, dal momento che mi piacerebbe vedere questo corr ect in AR direttamente ... ma fa il trucco.