Netzke Grid rows from the result of a DB query - netzke

I need to create an interface which contains a Form and an empty Grid. When I select the values in the Form and submits a query needs to created using 3 DB tables and the result of the query should be populated in the grid.
Note:- All the form fields are dependent combo boxes.
Any Idea?
Thanks.
Update1:-
Three models A, B, C
class A < ActiveRecord::Base
has_many :B
end
class B < ActiveRecord::Base
belongs_to :A
end
class C < ActiveRecord::Base
belongs_to :A
end
In table C, I have three columns "left (integer), right(integer) and version(string, eg:- 33.77.198.1)".
In table B, I have a column named "original (integer)".
Now my form should be
1) combo box - A - field "name" - Label Name
2) combo box - C - field "version" - Label Version 1
3) combo box - C - field "version" - Label Version 2
Now when select all these 3 values an Submit then a query should be created and the results should displayed in my Grid.
Query: -
SELECT a.name,c.version,
b.name, b.original
FROM B b
inner join C c on c.a_id = b.a_id
inner join A a on a.id = b.a_id
where b.a_id = 31
and b.original between
( select left from c where version = "347.0.112.227")
and
( select right from c where version = "347.0.112.529")
Here 31 - selected value from combo box A
347.0.112.227 - selected value from combo box B
347.0.112.529 - selected value from combo box C

Related

Rails 5 - ActiveRecord - Filtering has_many relation with includes and not exclude empty values

Let me explain the title.
I have model A that has_many model B.
I want to filter model B by month and year of the date while showing all model A's
so for example:
A1 -> 3 Bs
A2 -> 0 Bs
A3 -> 1 B
This is my query right now:
A.includes(:b_relation)
.where("extract(month from b.date) = #{month}").references(:b_relation)
.where("extract(year from b.date) = #{year}").references(:b_relation)
.all
It works! BUT it only gives me the A that has at least one B. The A's that have none don't show.
How can I make the query include the model A's that don't have any B's?
The query you're doing now is using a INNER JOIN which will exclude records from A that have no associated Bs. What you want instead is a LEFT OUTER JOIN—aka a LEFT JOIN. Left joins include all rows from the parent table, whether or not there are any associated records from the associated table.
I always find this image useful for visualizing SQL join types:
Rails 5 has a left_outer_joins method for this (alias: left_joins):
A.left_outer_joins(:b_relation)
In earlier versions of Rails, it's more manual (I'm just making up the table names here):
A.joins('LEFT OUTER JOIN "bs" ON "bs"."a_id" = "as"."id"')

Get parent objects without active childs

I've two models, Doctor and DoctorClinic where a doctor has_many clinics.
doctor.rb:
has_many :clinics, class_name: 'DoctorClinic', dependent: :destroy
doctor_clinic.rb
belongs_to :doctor
DoctorClinic have doctor_id and a boolean active field.
What I want:
I want to get all doctors which does not have any active (active field is true) clinics. If a doctor have two clinics, out of which one is active and other is inactive then doctor should not be selected.
Doctor record will be selected if,
there's no clinics at all
if there's any clinic but all are inactive, i.e. all have active false.
Doctor will not be selected if,
there's any active clinics.
What I've tried so far:
Try 1:
scope :incomplete_doctors, -> { includes(:clinics)
.where("( doctor_clinics.id IS NULL ) OR
( doctor_clinics.id IS NOT NULL AND
doctor_clinics.active=?)", false )
}
Try 2:
scope :incomplete_doctors, -> { where("id NOT IN (?)", self.includes(:clinics)
.where("( doctor_clinics.doctor_id IS NULL ) OR
( doctor_clinics.doctor_id IS NOT NULL AND
doctor_clinics.active=?)", false )
.select(:id))
}
Try 3:
SELECT "doctors".* FROM "doctors"
LEFT OUTER JOIN "doctor_clinics" ON "doctor_clinics"."doctor_id" = "doctors"."id"
WHERE ( ( doctor_clinics.id IS NULL ) OR
( doctor_clinics.id IS NOT NULL AND
doctor_clinics.active='f'))
GROUP BY doctors.id
HAVING 'true' <> ANY(array_agg(DISTINCT doctor_clinics.active::TEXT));
Success:
I'm able to achieve desired output using following method, but I want to achieve this using a SQL query.
def active_clinics
clinics.active_clinics # active_clinics is a scope in Clinic model while give all active clinics
end
def self.incomplete_doctors
(Doctor.all.map { |d| d unless d.active_clinics.present? }).compact
end
Something like this should do the trick in pure SQL
SELECT *
FROM doctors
WHERE NOT EXISTS (
SELECT 1
FROM doctor_clinics
WHERE
doctor_clinics.doctor_id = doctors.id
AND doctor_clinics.active = true
)
You should be able to use it with find_by_sql:
Doctor.find_by_sql(SQL)
Have no Rails 3 project to actually test it;-)

SQL + Rails - is it posible to get a collection of objects with the values of attributes from other table columns

class Baby
belongs_to :child
attr :is_public
scope :public, includes(:child).merge(Child.public).where('babies.is_public IS TRUE')
end
class Child
belongs_to :parent
attr :is_public
scope: public, where ???
def is_public; read_attribute(:is_public).blank? ? self.parent.is_public : super(); end
end
class Parent
has_many :children
attr :is_public
end
Is it possible to get a collection of objects of Child
where if the value of attribute_a of child is NULL
it should get the value from parents.attribute_a
in one sql statement
I think, basic SQL logic should by like this:
SELECT c.*,p.* FROM child c
INNER JOIN parent p ON p.id = c.parent_id
LEFT JOIN baby b on b.id = c.baby_id
WHERE b.id IS NULL
if you want to use SQL query instead of ruby class, you can use:
query_result = ActiveRecord::Base.connection.execute('Your query')
Hope this helps

Rails: join query on association with class_name

class A < ActiveRecord::Base
has_one :b, class_name: "Something::B"
end
module Something
class B < ActiveRecord::Base
end
end
Assuming above class structure with actual table names a and something_b, I want to create the following SQL query.
SELECT "a".* FROM "a"
INNER JOIN "something_b" ON
"something_b"."a_id" = "a"."id"
WHERE "something_b"."some_column" = "some_value" LIMIT 1
I tried something along the lines of
A.joins(:b).find_by(b: { some_column: 'some_value' })
but the resulting query is as follows, which has "b" instead of "something_b" in the WHERE clause.
SELECT "a".* FROM "a"
INNER JOIN "something_b" ON
"something_b"."a_id" = "a"."id"
WHERE "b"."some_column" = "some_value" LIMIT 1
Is there a way to do it without explicitly specifying the table name as follows?
A.joins(:b).find_by(something_b: { some_column: 'some_value' })
You can try one of the following solution.
In module you can define the following method
def self.table_name_prefix
'something_'
end
or
In class you can set table name as
self.table_name = 'something_b'
If your model name and the table names do not match rails conventions, which in its simplest form table_name = SomeClass.name.pluralize.downcase, then you need to define the actual table name in your model.
module Something
class B < ActiveRecord::Base
self.table_name = 'something_b'
end
end
Above change should pick up the right table name in to the generated query.

Problems with Rails 3 Active Record Query Interface with join on ID

I have been having a problem with the Rails 3 Active Record Query Interface. I have a lookup table (lookups), a Main table (through_references), and a through/join table called through_tables. Thus this is a HABTM configuration that I have set up using has_many :through.
Update: Of special note here is that when I am doing these joins, I have been joining on IDs, to provide filtering of records. It seems that this does not work with Active Record Query Interface. If you do not want to see the gory details of my travails, you can skip down to see my workaround below.
We are also going to have a number of Main Items (through_references table) should be able to have any combination of lookup items, and to conveniently be able to click the relevant lookup items say through check boxes.
I have posted the code on github. There is quite a lot more explanations on the github source code. to see the results, go to the lookups index page. Note that you will need to create the records using the scaffold code.
I also have the code up and running on heroku, with more explanations and examples.
class Lookup < ActiveRecord::Base
has_many :fk_references
has_many :through_tables
has_many :through_references, :through => :through_tables
attr_accessible :name, :value
end
class ThroughTable < ActiveRecord::Base
belongs_to :through_reference
belongs_to :lookup
attr_accessible :description, :through_reference_id, :lookup_id
end
class ThroughReference < ActiveRecord::Base
has_many :through_tables
has_many :lookups, :through => :through_tables
attr_accessible :description
end
If we want to have a listing if all the lookup items, and the Main Items that correspond with them, we can LEFT JOIN the ‘lookups’ table with the Main Items (through_references) table.
Corresponding SQL:
SELECT * FROM lookups
LEFT OUTER JOIN through_tables ON (lookups.id = through_tables.lookup_id AND through_tables.through_reference_id = 1)
LEFT OUTER JOIN through_references ON through_references.id = through_tables.through_reference_id
ORDER BY lookups.id
Returned records:
1;“Lookup Item 1”;“1”;“2012-06-06 17:14:40.819791”;“2012-06-06 17:14:40.819791”;1;1;1;“Main Item 1 has Lookup item 1”;“2012-06-06 17:17:31.355425”;“2012-06-06 17:17:31.355425”;1;“Main Item 1”;“2012-06-06 17:16:30.004375”;“2012-06-06 17:16:30.004375”
2;“Lookup Item 2”;“2”;“2012-06-06 17:14:59.584756”;“2012-06-06 17:14:59.584756”;;;;“”;“”;“”;;“”;“”;“”
3;“Lookup Item 3”;“3”;“2012-06-06 17:15:14.700239”;“2012-06-06 17:15:14.700239”;2;1;3;“Main Item 1 has Lookup item 3”;“2012-06-06 17:17:53.169715”;“2012-06-06 17:17:53.169715”;1;“Main Item 1”;“2012-06-06 17:16:30.004375”;“2012-06-06 17:16:30.004375”
This is what I expected.
=== Active Record Query Interface using custom left join
Lookup.joins(“LEFT OUTER JOIN through_tables ON (lookups.id = through_tables.lookup_id AND through_tables.through_reference_id = 1)” ).includes(:through_references).order(‘lookups.id’)
What is returned from Active Record Query Interface (note I navigate down through the Active Record hierarchy):
Lookup ID Lookup Name Lookup Value Through Table ID Through Table Description Main Item ID Main Item Description
1 Lookup Item 1 1 1 Main Item 1 has Lookup item 1 1 Main Item 1
1 Lookup Item 1 1 3 Main Item 2 has Lookup item 1 2 Main Item 2
2 Lookup Item 2 2 4 Main Item 2 has Lookup item 2 2 Main Item 2
3 Lookup Item 3 3 2 Main Item 1 has Lookup item 3 1 Main Item 1
This is NOT what I expected.
What we have here is identical to the simple left join (without the AND clause). This tells me that the AND clause is being ignored in the Active Record Query Interface.
=== Active Record Query Interface using find_by_sql approach
Lookup.find_by_sql("SELECT * FROM lookups LEFT OUTER JOIN through_tables ON (through_tables.lookup_id = lookups.id AND through_tables.through_reference_id = 1) LEFT OUTER JOIN through_references ON through_references.id = through_tables.through_reference_id ORDER BY lookups.value, through_references.id" )
What is returned from Active Record Query Interface (note I navigate down through the Active Record hierarchy)::
Lookup ID Lookup Name Lookup Value Through Table ID Through Table Description Main Item ID Main Item Description
1 Lookup Item 1 1 3 Main Item 2 has Lookup item 1 2 Main Item 2
1 Lookup Item 1 1 1 Main Item 1 has Lookup item 1 1 Main Item 1
Lookup Item 2 2 No through_tables entry
1 Lookup Item 3 3 3 Main Item 2 has Lookup item 1 2 Main Item 2
1 Lookup Item 3 3 1 Main Item 1 has Lookup item 1 1 Main Item 1
The results here are crazy!
Is this a BUG, is this the intended effects, or am I missing something ?
I hope there is a clean way of doing this, without having to generate two result sets, and merge them by code.
I have found a work-around. The issue seems to be that Active Record will not recognize joins that filter on an ID (LEFT OUTER JOIN xyz ON xyz.id = ID).
My work-around involves creating a stored procedure or function that takes the ID in as a parameter, does the join in the Database, and returns a nice flat recordset.
see: Heroku demo page (skip to bottom)
Note, I am not marking this as a solution, because this is a work-around, and nothing to do with active record.
Well, reading the github project, I see this:
What I really want to do is have a list of all of the lookup items,
and if there are matching Main Items, have them appended on to the
returned record, and if not, I want nulls. This is a technique that I
have used for over 10 years.
I'm thinking that problem is exactly that you want to do it that way, when it would be more natural to let rails eager loading handle it, and so you've gotten fixated on fetching everything in a single massive join.
What I would do is something like:
Lookup.where( .. insert any needed conditions here ...).includes(:through_tables)
Then ActiveQuery will then fetch all the Lookup in one query, and then use eager loading to fetch any associations named in the includes statement, one query per association.
Note I'm not saying that joins are bad, just saying that this is a more natural way to do it in rails. I like to use the Preloader http://apidock.com/rails/ActiveRecord/Associations/Preloader to separate out the decision about what to eager load from the decision about which data to fetch. I find that helpful in controllers - let the model decide what the conditions are, but let the controller decide which objects it'll need to eager load.
HTH