Rails find_by_sql not working - ruby-on-rails

I´m trying to do execute find_by_sql Rails method and it´s not working.
Query:
#boats = Boat.find_by_sql(["SELECT *
FROM boats b, ports p
WHERE p.name = ?
AND b.port_id = p.id", port])
puts #boats.inspect
Query returns two elements, however it´s the same element twice [id=1 and id=1]. If I run the query in the database then I got two different results [id=1 and id=2]

You seem to be selecting all columns of both the boats table and the ports table in order to create a set of Boat objects. Perhaps you mean to select b.*?

Related

What is the correct way to query data in SQLAlchemy (async) in case of multiple levels of relationships?

I'm trying to read data from related tables which are x levels deep and which have relationships specified, i.e.:
table A
table B
table C
table ABC
Table ABC has relationships ABC.a = A, ABC.b = B and ABC.c = C,
i.e. foreign keys ABC.aid = A.id, ABC.bid = B.id and ABC.cid = C.id.
aid, bid and cid in ABC are set unique using UniqueContstraint
relationship is using lazy="joined"
When I do select(ABC) I'm able to get all values from ABC and also from related tables, i.e.:
{ABC.a: {A}, ABC.b: {B}, ABC.c: {C}}
I have also table D which has a relationship to ABC (D.abcid = ABC.id) and I struggle to construct a correct select statement which would give me all data also from A, B and C. Actually I'm not sure if this should work or I missed / do not understand something in the documentation as I have tried various loading strategies, specified join_depth for D and ABC tables, etc. No matter what I'm getting:
sqlalchemy.exc.InvalidRequestError: The unique() method must be invoked on this Result, as it contains results that include joined eager loads against collections
I would like to get the data the same way as for 1st level relationship, i.e.:
{D.abc : {ABC.a: {A}, ABC.b: {B}, ABC.c: {C}}}
Is it possible or do I have to change the select query completely and just create multiple joins and manually pick all the values I need?
I'm able to get correct records from the database when I just take the generated select statement and use it directly in a DB shell (MariaDB) so I assume that the only issue is my lack of understanding of how SQL handles/presents these records internally.
The issue was using uselist=True in one of the models, all relationships are working perfectly down to the lowest level now.

How to check if my prepared statement in Rails is executing or not?

I have this prepared statement in Rails (postgres db):
conn = ActiveRecord::Base.connection.raw_connection
conn.prepare('query_fetch_dest_values',
"SELECT q.entity AS source_entity,
q.financial_institution AS source_financial_institution,
q.account_number AS source_account_number,
a2.entity AS destination_entity,
a2.financial_institution AS destination_financial_institution,
a2.account_number AS destination_account_number
FROM
(SELECT *
FROM forecast_entry_and_cash_positions
INNER JOIN accounts a ON forecast_entry_and_cash_positions.source_account_id = a.account_number
WHERE (input_type IN ($1) AND
a.financial_institution IN ($2) AND
forecast_entry_and_cash_positions.source_account_id IN ($3))
)q LEFT JOIN accounts a2 ON q.dest_account_id = a2.account_number")
search_destination_values =
conn.exec_prepared('query_fetch_dest_values',
[decode_input_type(params[:search_input_type]),
params[:search_source_financial_institution],
params[:search_source_account_number]
])
puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
puts search_destination_values.inspect
The above SQL is not getting printed on my console. So how do I get to know if my query is being executed or not?
In the inspect as shown in above code, this is what is getting printed on the console:
#<PG::Result:0x00007f9d32c528b8 status=PGRES_TUPLES_OK ntuples=0 nfields=6 cmd_tuples=0>
I learnt from this blog - https://blog.kiprosh.com/understanding-pg-result-object/ that nTuples=0 and cmd_tuples=0 in the above output means the number of records returned by the SQL is 0.
My query in pgadmin returns 3 records - why am I not getting any output here?
This documentation https://rubydoc.info/gems/pg/PG/Connection#exec_prepared-instance_method and stackoverflow blogs like this one -> Prepared Statement on Postgresql in Rails show how to pass dynamic values for filters when the filter condition is an = (equals) condition on the filtered columns.
However in my condition I have 3 IN filters. The documentation does not show an example for passing dynamic filters for IN clauses. In the above code snippet, is the way am passing dynamic filters for my 3 IN clauses correct? (Again, since my query is not getting printed on my console, I am unable to verify if my filters got added correctly to the SQL or not).

ActiveRecord PostgreSQL querying an Array

I have setup a PostgreSQL array field identical to this example on edgeguides. I am querying on these fields like:
Book.where("'fantasy' = ANY (tags)")
But what I need is to query the inverse of this; all records where tags does not include 'fantasy' (in this example).
Anyone have any guidance? I cannot find much documentation on working with a PostgreSQL array field outside of the aforementioned guide.
You can negate your condition
Book.where("NOT('fantasy' = ANY (tags))")
So you can modify query to get records with NULL records also:
Book.where("NOT('fantasy' = ANY (tags)) or tags IS NULL")
Also you can run these queries in psql and check results
SELECT * FROM book WHERE NOT('fantasy' = ANY (tags));
SELECT * FROM book WHERE NOT('fantasy' = ANY (tags)) OR tags IS NULL;
SELECT * FROM book WHERE 'fantasy' = ANY (tags)
Maybe there is no records without tag 'fantasy'?
Try this:
Book.where.not('tags #> ARRAY[?]', "fantasy")

Populate an active record collection with different SQLs on the same model in rails

I'm trying to populate an active record collection from several SQLs on the same model. The only thing that differs between the SQLs is the where clause. My models have a type_id. As an example I have
models = Model.where("type_id = ?", 1)
logger.debug 'models.count ' + models.count.to_s
m = Model.where("type_id = ?", 2)
models << m
logger.debug 'models.count ' + models.count.to_s
From that, my logfile shows me
SELECT COUNT(*) FROM "models" WHERE (type_id = 1)
models.count 1
SELECT COUNT(*) FROM "models" WHERE (type_id = 1)
models.count 1
The second SQL is not correct for my situation, I wanted
SELECT COUNT(*) FROM "models" WHERE (type_id = 2)
The only way I've found to get around this is to do Model.all, iterate over each and add the ones I want. This would be very time consuming for a large model. Is there a better way?
From the sounds of it, you're looking for any Model with a type_id of either 1 or 2. In SQL, you would express this as an IN subclause:
SELECT * FROM models WHERE type_id IN (1, 2);
In Rails, you can pass an array of acceptable values to a where call to generate the SQL IN statement:
Model.where(:type_id => [1, 2])
As stated by #ArtOfCode what you want is to do the query on one pass. That being said, what you are trying to do there won't work because when you are adding with << the object of your second query to the first one you are just appending the instance to the first collection. The object type of the resulting query is an ActiveRecord_Relation which happens to hold two instances of your custom models (in this case Model) but when you send / call count thats actually executing an ActiveRecord query.
How can you tell the difference? Well, if you do run that code you used and do:
models.count
You'll see that there's SQL executed for whatever the conditions of the query on models you did, however, if you do this:
models.length
You'll notice the result is 2, and the reason is because the length of the collection of your own objects which happens to be inside the ActiveRecord_Relation is indeed two, and that is what happens if you use <<; it'll add object instances to the relation but that does not mean that they are part of the query.
You could even do this:
models << Model.new
And calling models.length would effectively return 3 because that is the amount of instances of your model that are contained within the relation, again, not a part of the query. So as you can see you can even add new object instances which have not even been saved to the database.
TL;DR if you want to query objects that are stored in the database do it on the query itself, or chain conditions at once, but don't try to mix activerecord relation collections.

Rails: AND operator in a has_many association

My relationship is a Client can have many ClientJobs. I want to be able to find clients that perform both Job a and Job b. I'm using 3 select boxes so I can pick a maximum of three jobs to select from. The select boxes are populated from the database.
I know how to test for 1 job with the query below. But I need a way to use an AND operator to test that both jobs exist for that client.
#clients = Client.includes("client_jobs").where(
client_jobs: { job_name: params[:job1]})
Unfortunately it's easy to do an IN operation like below, but I'm thinking the syntax for AND should be similar....I hope
#lients = Client.includes("client_jobs").where(
client_jobs: { job_name: [params[:job1], params[:job2]]})
EDIT: Posting the sql statement that hits the database from the answer below
Core Load (0.6ms) SELECT `clients`.* FROM `clients`
CoreStatistic Load (1.9ms) SELECT `client_jobs`.* FROM `client_jobs`
WHERE `client_jobs `.`client_id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10,........)
The second query runs through every client_job in the database. It's never tested against the params[:job1], params[:job2] etc. So #clients returns nil crashing my view template
(undefined method `map' for nil:NilClass
In my opinion, a better approach then self-joins is to simply join ClientJobs and then use GROUP BY and HAVING clauses to filter out only those records that exactly match the given associated records.
performed_jobs = %w(job job2 job3)
Client.joins(:client_jobs).
where(client_jobs: { job_name: performed_jobs }).
group("clients.id").
having("count(*) = #{performed_jobs.count}")
Let's walk through this query:
first two clauses join the ClientJobs to Clients and filter out only those, that have any of the three jobs defined (it uses the IN clause)
next, we group these joined records by Client.id so that we get the clients back
finally, the having clause ensures we only return those clients that had exactly 3 ClientJob records joined in, i.e. only those that had all the three client jobs defined.
It is the trick with HAVING(COUNT(*) = ...) that turns the IN clause (which is essentially an OR-ed list of options) into a "must have all these" clause.
To do this in a single SQL query try the following:
jobs_with_same_user = ClientJob.select(:user_id).where(job_name: "<job_name1>", user_id: ClientJob.select(:user_id).where(job_name: "<job_name2>"))
#clients = Client.where(id: jobs_with_same_user)
Here's what this query is doing:
Select the user_ids of all Client jobs with [job_name2]
Select the user_ids of all Client jobs with user_id IN result set from (1) AND having [job_name1]
Select all users with using (2) as a subquery.
Not many know this but Rails 4+ supports subqueries. Basically this is a self join acting as subquery for the clients:
SELECT *
FROM clients
WHERE id IN <jobs_with_same_user>
Also, I'm not sure if you're referencing the client_jobs association in your view, but if you are, add the includes statement to avoid an N+1 query:
#clients = Client.includes(:client_jobs).where(id: jobs_with_same_user)
EDIT
If you prefer, the same result can be achieved with a self-referencing inner join:
jobs_with_same_user = ClientJob
.select("client_jobs.user_id AS user_id")
.joins("JOIN client_jobs inner_client_jobs ON inner_client_jobs.user_id=client_jobs.user_id")
.where(client_jobs: { job_name: "<first_job_name1>" }, inner_client_jobs: { job_name: "<job_name2>" })
#clients = Client.where(id: jobs_with_same_user)

Resources