2012-10-03 13 views
10

Tengo un modelo de evaluación. La evaluación tiene muchos puntajes. Cada vez que se crea una nueva evaluación, se crea un registro de puntaje para cada usuario que necesita una evaluación (ver a continuación el método actual que estoy utilizando para hacer esto). Así, por ejemplo, se pueden crear 40 registros de puntaje a la vez. El propietario de la evaluación actualiza cada registro de puntaje con el puntaje del usuario.Rails 3, instrucción de inserción SQL personalizada sin formato

Estoy buscando utilizar SQL sin procesar porque cada inserción es su propia transacción y es lenta.

me gustaría la conversión en una instrucción de inserción de masas utilizando SQL prima:

def build_evaluation_score_items 
    self.job.active_employees.each do |employee| 
    employee_score = self.scores.build 
    employee_score.user_id = employee.id 
    employee_score.save 
    end 
end 

Alguna idea de cómo se puede hacer esto? Intenté adaptar una muestra de código del sitio de Coffee Heated de Chris Heald pero, sin dados.

¡Gracias a todos los que quieran ayudar!

EDIT 1

I olvidado mencionar el método actual se envuelve en una transacción.

Así que, esencialmente, estoy tratando de agregar esto al bloque de código para que todo esté insertado en una declaración (** Este fragmento de código proviene del sitio Coffee de Chris Heald que discutió el tema. Yo haría la pregunta allí, pero el mensaje es> 3 años de edad):.

inserts = [] 
TIMES.times do 
    inserts.push "(3.0, '2009-01-23 20:21:13', 2, 1)" 
end 
sql = "INSERT INTO user_node_scores (`score`, `updated_at`, `node_id`, `user_id`)VALUES #{inserts.join(", ")}" 

que estaría feliz de mostrar el código de algunos de mis intentos que no funcionan ...

Gracias de nuevo!

Bueno, he improvisado algo que se parece al código anterior, pero me sale un error no válido instrucción SQL alrededor de la porción ('evaluation_id'. ¿Alguna idea?

def build_evaluation_score_items 
    inserts = [] 
    self.job.active_employees.each do |employee| 
    inserts.push "(#{self.id}, #{employee.id}, #{Time.now}, #{Time.now})" 
    end 
    sql = "INSERT INTO scores ('evaluation_id', `user_id`, 'created_at', `updated_at`)VALUES #{inserts.join(", ")}" 
    ActiveRecord::Base.connection.execute(sql) 
end 

Cualquier idea de lo que en el ? por encima de código SQL es la causa del error

+1

la parte created_at no funciona para mí, ya que es la creación de una cadena que postgres no es el análisis. 'ActiveRecord :: StatementInvalid: PG :: SyntaxError: ERROR: error de sintaxis en o cerca de" 07 " LINEA 4: (1, 1, 100, 2017-02-20 07:00:56 +0000, 2017-02-20 07 ... ' – Gambo

Respuesta

17

Bueno, después de mucho ensayo y error, aquí está la respuesta final. Lo bueno es que todos los registros se insertan a través de una declaración. Por supuesto, las validaciones se omiten (por lo tanto, esto no será apropiado si se requieren validaciones de modelo en create) pero en mi caso, eso no es necesario porque todo lo que hago es configurar el registro de puntaje para la evaluación de cada empleado. Por supuesto, las validaciones funcionan como se espera cuando el líder del trabajo actualiza el puntaje de evaluación del empleado.

def build_evaluation_score_items 
    inserts = [] 
    time = Time.now.to_s(:db) 
    self.job.active_employees.each do |employee| 
    inserts.push "(#{self.id}, #{employee.id}, '#{time}')" 
    end 
    sql = "INSERT INTO scores (evaluation_id, user_id, created_at) VALUES #{inserts.join(", ")}" 
    ActiveRecord::Base.connection.execute(sql) 
end 
+0

Te usé solución para armar algo para mis propias necesidades. Estaba haciendo algunas pruebas de rendimiento, y necesitaba completar el DB con muchos datos aleatorios (millones de filas) * rápidamente * (es decir, sin utilizar ActiveRecord). – user664833

+1

Esto no funciona para mí porque 'time' tiene un resultado incorrecto. Postgres está lanzando esto: 'ActiveRecord :: StatementInvalid: PG :: SyntaxError: ERROR: error de sintaxis en o cerca de" 07 " LINE 4: (1, 1, 100, 2017-02-20 07:04:11, 2017- 02-20 07:04:11 ... ' – Gambo

+0

Use la inserción para borrar la caché. – MRifat

5

Creo que lo que estás buscando es:

def build_evaluation_score_items 
    ActiveRecord::Base.transaction do 
    self.job.active_employees.each do |employee| 
     employee_score = self.scores.build 
     employee_score.user_id = employee.id 
     employee_score.save 
    end 
    end 
end 

Todas las transacciones están automáticamente niño "empujados" a la tran padres sacción. Esto evitará la sobrecarga de tantas transacciones y debería aumentar el rendimiento.

Puede leer más sobre las transacciones de ActiveRecord here.

ACTUALIZACIÓN

Lo sentimos, no he entendido bien. Manteniendo la respuesta anterior para la posteridad. Pruebe esto:

def build_evaluation_score_items 
    raw_sql = "INSERT INTO your_table ('user_id', 'something_else') VALUES " 
    insert_values = "('%s', '%s')," 
    self.job.active_employees.each do |employee| 
    raw_sql += insert_values % employee.id, "something else" 
    end 
    ActiveRecord::Base.connection.execute raw_sql 
end 
+0

Gracias por la sugerencia. Me olvidé de señalar en mi publicación que actualmente tengo la declaración envuelta en una transacción en el código, pero que aún no es óptima, ya que todavía ejecuta las instrucciones de inserción para cada empleado. El único beneficio para la transacción es que el índice db no está actualizado. –

+0

Actualicé mi respuesta con una posible solución. Por favor, avíseme si eso lo ayuda. –

+0

Hola Chuck - Estoy de acuerdo con mantener el primer artículo allí, por eso El método de transacción es lógico. Dicho esto, desafortunadamente, me aparece el siguiente error: método no definido 'execute' para ActiveRecord :: Base: Class. Buscaré el comando de ejecución apropiado y reportaré. –

6

En lugar de construir SQL directamente (y abrirse a la inyección de SQL y otros temas), yo recomendaría el activerecord-import gem. Puede emitir comandos de varias filas INSERT, entre otras estrategias.

A continuación, podría escribir algo como:

def build_evaluation_score_items 
    new_scores = job.active_employees.map do |employee| 
    scores.build(:user_id => employee.id) 
    end 
    Score.import new_scores 
end 
Cuestiones relacionadas