Question

I have 4 tables:

key: id, name

project: id, name

project_key: id, key_id, project_id

project_report: id, status, project_id, key_id

project_c_report: id, status, project_id, key_id, c_id

I want to produce a report using those tables: The output should be:

Key.name, project_report.status, project_c_report.status

I was able to do this by getting all the keys from a project, and loop over them

array = []
project.keys.each do |k|
 p =  ProjectReport.where(keyword_id: k, project_id: p.id).map(&:status)
 c =  ProjectCReport.where(keyword_id: k, project_id: p.id, c_id:1).map(&:status)
 array << {name: k.name, pr: p, pcr: c} 
end
array

The problem is that I am doing a lot of selects and everything is slow, can someone help me please with a better way of doing this.

Thank you

Was it helpful?

Solution

First, create a function in your DataBase. This is just a brief example, and also its done in PostgreSQL but shouldnt difer much from MySQL, SQLServer, etc

Function get_myreport(key_id integer, project_id integer [As many params as you'd like the function to get))

pProject    ALIAS FOR $1;
pKey        ALIAS FOR $2;

BEGIN

CREATE TEMP TABLE IF NOT EXISTS tmp_project_report(id integer, project_name character varying, *All the values you want to see in the report);
TRUNCATE tmp_project_report;

INSERT INTO tmp_project_report(all the columns)
SELECT a.table1_fields, b.table2_fields, c.table3_fields, d.table4_fields, e.table5_fields
FROM table1 a, table2 b, table3 c, table4 d, table5 e
WHERE
a.key = pKey
AND b.project_key = pProject

END;

Then, in your controller's method you call the up the function like this

myFunction = ActiveRecord:Base.connection.execute = "Select get_myreport("param1, param2, etc...")

You will have to make a model where you put all the fields that are on the temp_table you've made, and also you will set the temp_table as the self.table_name

Then, in your view, you'd only have to iterate on your collection and display the values accordingly

@report = TempTable.all

<% @report.each_do |report| %>
  <% report.value1 %>
  <% etc... %>
<% end %>

OTHER TIPS

Figure out the database query, then query the database directly from your model:

def records
  connection = ActiveRecord::Base.connection
  records = connection.select %Q {
    SELECT key.name, project_report.status, project_c_report.status
    FROM ...
    JOIN ...
    ;
  }
  records
end

Here is something you can try if you choose to keep this within Rails (note that the following query is untested and is shown for concept only):

report_data = Project.joins(project_key: :key)
  .joins('left join project_reports on project_keys.project_id = project_reports.project_id and project_keys.key_id = project_reports.key_id 
          left join project_c_reports on project_keys.project_id = project_c_reports.project_id and project_keys.key_id = project_c_reports.key_id')
  .where('project_c_reports.c_id = ?', 1)
  .select('projects.name, project_reports.status as report_status, project_c_reports.status as c_report_status')

This should give you an array of Project objects each including the selected three attributes name, report_status, c_report_status. To get these values in an array of these three elements you could do:

report_data.map { |p| [ p.name, p.report_status, p.c_report_status ] }

The type of join for the query depends on your requirement. Given the index are in place the query should be better compared to how it looks in code!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top