I have two tables:
Player --> has_many FantasyStarts
FantasyStarts --> belongs_to Player
In english, I am trying to find all SeasonStats from the last year and week that were the first entry for that player in the whole table (ie who started last week and it was their first 'start' ever recorded in the table). I have constructed a bare sql query that works great, but I am trying to improve my rails query skills:
FantasyStart.includes(:player).find_by_sql("with count_table as (select player_id, count(*) as num_starts from fantasy_starts where position != 'BN' group by player_id) select * from fantasy_starts join count_table on fantasy_starts.player_id = count_table.player_id where week = 13 and year = 2019 and count_table.num_starts = 1 and position != 'BN' ")
I have also figure out a 'rails way' of creating the map of counts I would join to:
FantasyStart.where.not(position: 'BN').group(:player_id).count
which yields:
=> {184=>2, 2562721=>5, 2540215=>12, 100004=>57, 100001=>26, 100006=>62, 2505785=>5, 2561029=>1, 2541316=>1, 2558954=>1, 2552408=>12, 2532820=>60, 2507999=>25, 2506194=>16, 2505600=>18, 2532977=>6, 2507164=>75, 2495441=>4, 100022=>23, 2543704=>2, 2532807=>1, 81288=>1, 2550658=>4, 2506386=>15, 2560809=>11, 2533349=>4, 2560735=>3, 2557976=>19, 2556521=>15,....
hypothetically i could use the map above to filter the main query and only include player_ids that have a count of 1 in that map.
any good ideas for a next step?
Answering to your answer;
That's because you're chaining pluck to your "nested" query, that way ActiveRecord isn't able to perform a subquery, and instead of that it makes another query and use the result of that, which is an array to use with an IN clause.
Try instead removing the pluck, leaving just select:
FantasyStart.where(week: 13, year: 2019).where.not(position: 'BN').where(player_id: FantasyStart.where.not(position: 'BN').group(:player_id).having('count(*) = 1').select(:player_id))
It should generate something like this:
SELECT fantasy_starts.*
FROM fantasy_starts
WHERE fantasy_starts.week = $1
AND fantasy_starts.year = $2
AND fantasy_starts.position != $3
AND fantasy_starts.player_id IN (
SELECT fantasy_starts.player_id
FROM fantasy_starts
WHERE fantasy_starts.position != $4
GROUP BY fantasy_starts.player_id
HAVING (count(*) = 1)
) [[week, 13], [year, 2019], [position, BN], [position, BN]]
It seems you could shorten the query by removing the != from the subquery:
FantasyStart
.where.not(position: 'BN')
.where(player_id: FantasyStart
.group(:player_id)
.having('count(*) = 1')
.select(:player_id), week: 13, year: 2019)
hey i think i made progress curious what one of you more experience folk might think:
FantasyStart.where(week: 13, year: 2019).where.not(position: 'BN').where(player_id: FantasyStart.select(:player_id).where.not(position: 'BN').group(:player_id).having('count(*) = 1').pluck(:player_id))
this generates:
(8.4ms) SELECT "fantasy_starts"."player_id" FROM "fantasy_starts" WHERE "fantasy_starts"."position" != $1 GROUP BY "fantasy_starts"."player_id" HAVING (count(*) = 1) [["position", "BN"]]
FantasyStart Load (2.4ms) SELECT "fantasy_starts".* FROM "fantasy_starts" WHERE "fantasy_starts"."week" = $1 AND "fantasy_starts"."year" = $2 AND "fantasy_starts"."position" != $3 AND "fantasy_starts"."player_id" IN ($4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $40, $41, $42, $43, $44, $45, $46, $47, $48, $49, $50, $51, $52, $53, $54, $55, $56, $57, $58, $59, $60, $61, $62, $63, $64, $65, $66, $67, $68, $69, $70, $71, $72, $73, $74, $75, $76, $77, $78, $79, $80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $90, $91, $92, $93, $94, $95, $96, $97, $98, $99, $100, $101, $102, $103, $104, $105, $106, $107, $108, $109, $110, $111, $112, $113, $114, $115, $116, $117, $118, $119) LIMIT $120
this is still two queries so i know it could be better...
Related
In my Rails 5 and Ruby 2.4 app I've got two models CaregiverAuthorization and Registrant:
# caregiver_authorization
class CaregiverAuthorization < ApplicationRecord
belongs_to :registrant
scope :pending, (-> { where(status: [statuses[:pending], statuses[:pending_renewal]]) })
end
# registrant
class Registrant < User
has_many :caregiver_authorizations,
(-> { order('created_at asc') })
def full_name
[first_name, last_name].join(' ')
end
end
Now I want to have all pending caregiver_authorizations ordered by registrant.full_name. Pretty simple, so I did:
CaregiverAuthorization.includes(:registrant).order("registrants.full_name")
Surprisingly I got below error:
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "registrants"
LINE 1: ...id" AND "users"."type" IN ('Registrant') ORDER BY registrant...
^
What did I missed?
[Edit]
Full error message:
SQL (0.8ms) SELECT "caregiver_authorizations"."id" AS t0_r0, "caregiver_authorizations"."registrant_id" AS t0_r1, "caregiver_authorizations"."reviewer_id" AS t0_r2, "caregiver_authorizations"."reviewed_at" AS t0_r3, "caregiver_authorizations"."status" AS t0_r4, "caregiver_authorizations"."created_at" AS t0_r5, "caregiver_authorizations"."updated_at" AS t0_r6, "users"."id" AS t1_r0, "users"."external_system_id" AS t1_r1, "users"."external_system_type" AS t1_r2, "users"."type" AS t1_r3, "users"."login_id" AS t1_r4, "users"."first_name" AS t1_r5, "users"."last_name" AS t1_r6, "users"."status" AS t1_r7, "users"."minor" AS t1_r8, "users"."created_at" AS t1_r9, "users"."updated_at" AS t1_r10, "users"."paper_entry" AS t1_r11, "users"."date_of_birth" AS t1_r12, "users"."caregiver_limit_override" AS t1_r13, "users"."patient_limit_override" AS t1_r14 FROM "caregiver_authorizations" LEFT OUTER JOIN "users" ON "users"."id" = "caregiver_authorizations"."registrant_id" AND "users"."type" IN ('Registrant') ORDER BY registrants.full_name
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "registrants"
LINE 1: ...id" AND "users"."type" IN ('Registrant') ORDER BY registrant...
^
: SELECT "caregiver_authorizations"."id" AS t0_r0, "caregiver_authorizations"."registrant_id" AS t0_r1, "caregiver_authorizations"."reviewer_id" AS t0_r2, "caregiver_authorizations"."reviewed_at" AS t0_r3, "caregiver_authorizations"."status" AS t0_r4, "caregiver_authorizations"."created_at" AS t0_r5, "caregiver_authorizations"."updated_at" AS t0_r6, "users"."id" AS t1_r0, "users"."external_system_id" AS t1_r1, "users"."external_system_type" AS t1_r2, "users"."type" AS t1_r3, "users"."login_id" AS t1_r4, "users"."first_name" AS t1_r5, "users"."last_name" AS t1_r6, "users"."status" AS t1_r7, "users"."minor" AS t1_r8, "users"."created_at" AS t1_r9, "users"."updated_at" AS t1_r10, "users"."paper_entry" AS t1_r11, "users"."date_of_birth" AS t1_r12, "users"."caregiver_limit_override" AS t1_r13, "users"."patient_limit_override" AS t1_r14 FROM "caregiver_authorizations" LEFT OUTER JOIN "users" ON "users"."id" = "caregiver_authorizations"."registrant_id" AND "users"."type" IN ('Registrant') ORDER BY registrants.full_name
Maybe this will be helpfully:
2.4.5 :065 > CaregiverAuthorization.includes(:registrant).order("registrants.full_name").to_sql
=> "SELECT \"caregiver_authorizations\".\"id\" AS t0_r0, \"caregiver_authorizations\".\"registrant_id\" AS t0_r1, \"caregiver_authorizations\".\"reviewer_id\" AS t0_r2, \"caregiver_authorizations\".\"reviewed_at\" AS t0_r3, \"caregiver_authorizations\".\"status\" AS t0_r4, \"caregiver_authorizations\".\"created_at\" AS t0_r5, \"caregiver_authorizations\".\"updated_at\" AS t0_r6, \"users\".\"id\" AS t1_r0, \"users\".\"external_system_id\" AS t1_r1, \"users\".\"external_system_type\" AS t1_r2, \"users\".\"type\" AS t1_r3, \"users\".\"login_id\" AS t1_r4, \"users\".\"first_name\" AS t1_r5, \"users\".\"last_name\" AS t1_r6, \"users\".\"status\" AS t1_r7, \"users\".\"minor\" AS t1_r8, \"users\".\"created_at\" AS t1_r9, \"users\".\"updated_at\" AS t1_r10, \"users\".\"paper_entry\" AS t1_r11, \"users\".\"date_of_birth\" AS t1_r12, \"users\".\"caregiver_limit_override\" AS t1_r13, \"users\".\"patient_limit_override\" AS t1_r14 FROM \"caregiver_authorizations\" LEFT OUTER JOIN \"users\" ON \"users\".\"id\" = \"caregiver_authorizations\".\"registrant_id\" AND \"users\".\"type\" IN ('Registrant') ORDER BY registrants.full_name"
Please note that includes does not necessarily join tables in the database query. It is built to avoid N+1 queries and it might happen that it simply runs two queries (one for the caregiver_authorizations and another for the associated registrants.
When you really need both tables in one query because you need to filter or order by them then you need to use joins. Additionally, the table is not named registrants but users because Registrant is a subclass of User. And you cannot order by full_name because full_name is not a database query but a method in your model. To order by full_name you need to simulate the behavior of that method in SQL.
I guess the following should work for you:
CaregiverAuthorization
.pending
.joins(:registrant)
.order(Arel.sql("LOWER(CONCAT(users.first_name, ' ', users.last_name))"))
I am building a Rails app with a webpage that shows a list of QuoteDocuments sorted by the quotedocument's PDF created date, which is attached via ActiveStorage
class Quote < ApplicationRecord
has_many :quote_documents, dependent: :destroy
end
class QuoteDocument < ApplicationRecord
has_one_attached :document
end
This is the line of code I am using that gives me an error
#quote.quote_documents.includes(:document_attachment).order('active_storage_blobs.filename ASC')
And this is the error
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: missing FROM-clause entry for table "active_storage_blobs")
And the SQL statement
: SELECT "quote_documents"."id" AS t0_r0, "quote_documents"."quote_id" AS t0_r1, "quote_documents"."version" AS t0_r2, "quote_documents"."created_at" AS t0_r3, "quote_documents"."updated_at" AS t0_r4, "quote_documents"."document_file_name" AS t0_r5, "quote_documents"."document_content_type" AS t0_r6, "quote_documents"."document_file_size" AS t0_r7, "quote_documents"."document_updated_at" AS t0_r8, "quote_documents"."document_type" AS t0_r9, "quote_documents"."description" AS t0_r10, "quote_documents"."user_id" AS t0_r11, "active_storage_attachments"."id" AS t1_r0, "active_storage_attachments"."name" AS t1_r1, "active_storage_attachments"."record_type" AS t1_r2, "active_storage_attachments"."record_id" AS t1_r3, "active_storage_attachments"."blob_id" AS t1_r4, "active_storage_attachments"."created_at" AS t1_r5 FROM "quote_documents" LEFT OUTER JOIN "active_storage_attachments" ON "active_storage_attachments"."record_id" = "quote_documents"."id" AND "active_storage_attachments"."record_type" = $1 AND "active_storage_attachments"."name" = $2 WHERE "quote_documents"."quote_id" = $3 AND "quote_documents"."document_type" = $4 ORDER BY active_storage_blobs.filename ASC LIMIT $5
Any idea what I should be doing to fix this?
You need to use include in your original statement that sets the #quote, rather than from the #quote variable:
#quote = Quote.includes(quote_documents: {document_attachment: :blob}).find(params[id])
in your controller
then in your view you should just be able to use:
#quote.quote_documents.order('active_storage_blobs.filename ASC')
I didn't have any idea what was going wrong with this query:
#customers =
Customer.includes(:phone_numbers, :emails)
.select("first_name, last_name, zip_code, customers.id, street1, street2")
.where(:merchant_id => environment)
.where("phone_numbers.number ~* :key",
:key => key, :digits => digits)
It was working fine in Rails3.
I stumbled onto some documentation here that helped me resolve it in Rails4, but I'd like to know if there is a better way to write this query.
It produces the following (this is gnarly...):
"SELECT first_name, last_name, zip_code, customers.id, street1, street2, "customers"."id" AS t0_r0, "customers"."account_id" AS t0_r1, "customers"."first_name" AS t0_r2, "customers"."last_name" AS t0_r3, "customers"."street1" AS t0_r4, "customers"."street2" AS t0_r5, "customers"."city_id" AS t0_r6, "customers"."city" AS t0_r7, "customers"."state" AS t0_r8, "customers"."state_id" AS t0_r9, "customers"."zip_code" AS t0_r10, "customers"."country" AS t0_r11, "customers"."municipality" AS t0_r12, "customers"."latitude" AS t0_r13, "customers"."longitude" AS t0_r14, "customers"."location" AS t0_r15, "customers"."customer_type" AS t0_r16, "customers"."notes" AS t0_r17, "customers"."birthday" AS t0_r18, "customers"."merchant_id" AS t0_r19, "customers"."gmaps" AS t0_r20, "customers"."marketing" AS t0_r21, "customers"."communication_method" AS t0_r22, "customers"."account_status" AS t0_r23, "customers"."account_type" AS t0_r24, "customers"."created_at" AS t0_r25, "customers"."updated_at" AS t0_r26, "customers"."api_customer_id" AS t0_r27, "customers"."api_updated_at" AS t0_r28, "phone_numbers"."id" AS t1_r0, "phone_numbers"."customer_id" AS t1_r1, "phone_numbers"."account_id" AS t1_r2, "phone_numbers"."contact_id" AS t1_r3, "phone_numbers"."number" AS t1_r4, "phone_numbers"."country_code" AS t1_r5, "phone_numbers"."area_code" AS t1_r6, "phone_numbers"."extension" AS t1_r7, "phone_numbers"."created_at" AS t1_r8, "phone_numbers"."updated_at" AS t1_r9, "emails"."id" AS t2_r0, "emails"."customer_id" AS t2_r1, "emails"."account_id" AS t2_r2, "emails"."contact_id" AS t2_r3, "emails"."address" AS t2_r4, "emails"."created_at" AS t2_r5, "emails"."updated_at" AS t2_r6
FROM "customers"
LEFT OUTER JOIN "phone_numbers" ON "phone_numbers"."customer_id" = "customers"."id"
LEFT OUTER JOIN "emails" ON "emails"."customer_id" = "customers"."id"
WHERE "customers"."merchant_id" = 29 AND (phone_numbers.number ~* '424298')"
Since you aren't selecting any of the fields in phone_numbers or emails I think you want to use join not includes. Tricky bit is to make the LEFT OUTER work.
http://apidock.com/rails/v4.2.1/ActiveRecord/QueryMethods/joins
Something like this maybe.
Customer.
select("first_name, last_name, zip_code, customers.id, street1, street2").
joins("LEFT OUTER JOIN phone_numbers ON phone_numbers.customer_id = customers.id").
joins("LEFT OUTER JOIN emails ON emails.customer_id = customers.id").
where(:merchant_id => environment).
where("phone_numbers.number ~* :key", :key => key, :digits => digits)
My original Active Record Query is shown below with the query it produces...
Active Record Query
player.measurables.includes(:workout, :measurable_type, [workout: :player_workout_type])
.where(measurable_type_id: measurable_type.id)
.order('player_workout_types.priority ASC, measurables.value DESC').first
Result
SELECT [measurables].[id] AS t0_r0, [measurables].[workout_id] AS t0_r1, [measurables].[measurable_type_id] AS t0_r2, [measurables].[value] AS t0_r3, [measurables].[created_at] AS t0_r4, [measurables].[updated_at] AS t0_r5, [measurables].[imported_with_errors] AS t0_r6, [measurables].[import_key] AS t0_r7, [measurables].[order_by] AS t0_r8, [measurables].[reliability_id] AS t0_r9, [workouts_measurables].[id] AS t1_r0, [workouts_measurables].[player_id] AS t1_r1, [workouts_measurables].[date] AS t1_r2, [workouts_measurables].[description] AS t1_r3, [workouts_measurables].[player_workout_type_id] AS t1_r4, [workouts_measurables].[nfl_player_id] AS t1_r5, [workouts_measurables].[college_player_id] AS t1_r6, [workouts_measurables].[import_key] AS t1_r7, [workouts_measurables].[created_at] AS t1_r8, [workouts_measurables].[updated_at] AS t1_r9, [workouts_measurables].[recorder_id] AS t1_r10, [workouts_measurables].[field_conditions] AS t1_r11, [workouts_measurables].[comments] AS t1_r12, [workouts_measurables].[jersey] AS t1_r13, [workouts_measurables].[report_id] AS t1_r14, [workouts_measurables].[all_star_game_id] AS t1_r15, [player_workout_types].[id] AS t2_r0, [player_workout_types].[type] AS t2_r1, [player_workout_types].[description] AS t2_r2, [player_workout_types].[rising_seniors_event] AS t2_r3, [player_workout_types].[import_key] AS t2_r4, [player_workout_types].[created_at] AS t2_r5, [player_workout_types].[updated_at] AS t2_r6, [player_workout_types].[priority] AS t2_r7, [measurable_types].[id] AS t3_r0, [measurable_types].[is_primary] AS t3_r1, [measurable_types].[code] AS t3_r2, [measurable_types].[abbreviation] AS t3_r3, [measurable_types].[description] AS t3_r4, [measurable_types].[unit_id] AS t3_r5, [measurable_types].[created_at] AS t3_r6, [measurable_types].[updated_at] AS t3_r7, [measurable_types].[preferred_type] AS t3_r8, [measurable_types].[order_by] AS t3_r9, [measurable_types].[precision] AS t3_r10, [reliabilities].[id] AS t4_r0, [reliabilities].[code] AS t4_r1, [reliabilities].[description] AS t4_r2, [reliabilities].[priority] AS t4_r3, [reliabilities].[created_at] AS t4_r4, [reliabilities].[updated_at] AS t4_r5, [reliabilities].[is_active] AS t4_r6, [reliabilities].[translation] AS t4_r7 FROM [measurables] LEFT OUTER JOIN [workouts] [workouts_measurables] ON [workouts_measurables].[id] = [measurables].[workout_id] LEFT OUTER JOIN [player_workout_types] ON [player_workout_types].[id] = [workouts_measurables].[player_workout_type_id] LEFT OUTER JOIN [measurable_types] ON [measurable_types].[id] = [measurables].[measurable_type_id] LEFT OUTER JOIN [reliabilities] ON [reliabilities].[id] = [measurables].[reliability_id] INNER JOIN [workouts] ON [measurables].[workout_id] = [workouts].[id] WHERE [workouts].[player_id] = #0 AND [measurables].[measurable_type_id] = #1 ORDER BY reliabilities.priority, player_workout_types.priority ASC, measurables.value DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY', N'#0 int, #1 int', #0 = 203646, #1 = 203
Active Record Query to put NULLs Last
player.measurables.includes(:workout, :measurable_type, :reliability, [workout: :player_workout_type])
.where(measurable_type_id: measurable_type.id)
.order('case when reliabilities.priority is not null then 0 else 99 end ASC, player_workout_types.priority ASC, measurables.value DESC').first
Result
It errors out with the following...
TinyTds::Error: The multi-part identifier "reliabilities.priority" could not be bound.:
EXEC sp_executesql N'SELECT [measurables].* FROM [measurables] INNER JOIN [workouts] ON [measurables].[workout_id] = [workouts].[id] WHERE [workouts].[player_id] = #0 AND [measurables].[measurable_type_id] = #1 ORDER BY case when reliabilities.priority is not null then 0 else 99 end ASC, player_workout_types.priority ASC, measurables.value DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY', N'#0 int, #1 int', #0 = 203646, #1 = 203
It appears I incorrectly thought that the includes worked as a left join. After making the following corrections from the comments left this worked for me..
player.measurables.includes(:workout, :measurable_type, :reliability, [workout: :player_workout_type], :reliability)
.joins('left join reliabilities on reliabilities.id = measurables.reliability_id')
.where(measurable_type_id: measurable_type.id)
.order('case when reliabilities.priority is null then 99 else reliabilities.priority end ASC, measurables.value ASC').first
So this is what I have in my dev (SQLite) environment that works perfectly:
key = "%#{keywords}%"
listings = Listing.order(:headline)
listings = listings.includes(:neighborhood).where("headline like ? or neighborhoods.name like ?", key, key) if keywords.present?
But, when I try something similar on Heroku, I get a number of errors:
listings = listings.includes(:neighborhood).where("headline like ? or neighborhoods.name like ?", key, key)
ActiveRecord::ConfigurationError: Association named 'neighborhoods' was not found; perhaps you misspelled it?
I also tried the following & got the corresponding error:
> listings = Listing.includes(:neighborhood).where("headline like :keywords or neighborhood.name like :keywords", :keywords => "%#{keywords}%")
SQL (3.1ms) SELECT "listings"."id" AS t0_r0, "listings"."listing_category_id" AS t0_r1, "listings"."listing_type_id" AS t0_r2, "listings"."user_id" AS t0_r3, "listings"."boro_id" AS t0_r4, "listings"."neighborhood_id" AS t0_r5, "listings"."building_id" AS t0_r6, "listings"."term_id" AS t0_r7, "listings"."headline" AS t0_r8, "listings"."property_type_id" AS t0_r9, "listings"."unit_num" AS t0_r10, "listings"."common_charges" AS t0_r11, "listings"."taxes" AS t0_r12, "listings"."maintenance" AS t0_r13, "listings"."num_bedrooms" AS t0_r14, "listings"."num_bathrooms" AS t0_r15, "listings"."square_footage" AS t0_r16, "listings"."list_square_footage" AS t0_r17, "listings"."description" AS t0_r18, "listings"."condition_id" AS t0_r19, "listings"."pet_policy_id" AS t0_r20, "listings"."exposure_id" AS t0_r21, "listings"."floor_plan" AS t0_r22, "listings"."photo" AS t0_r23, "listings"."security" AS t0_r24, "listings"."coop_deductible" AS t0_r25, "listings"."flip_tax" AS t0_r26, "listings"."house_families" AS t0_r27, "listings"."house_extensions" AS t0_r28, "listings"."house_stories" AS t0_r29, "listings"."house_units" AS t0_r30, "listings"."created_at" AS t0_r31, "listings"."updated_at" AS t0_r32, "listings"."price" AS t0_r33, "neighborhoods"."id" AS t1_r0, "neighborhoods"."name" AS t1_r1, "neighborhoods"."created_at" AS t1_r2, "neighborhoods"."updated_at" AS t1_r3, "neighborhoods"."boro_id" AS t1_r4 FROM "listings" LEFT OUTER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
ActiveRecord::StatementInvalid: PG::Error: ERROR: missing FROM-clause entry for table "neighborhood"
LINE 1: ..._id" WHERE (headline like '%prospect heights%' or neighborho...
^
: SELECT "listings"."id" AS t0_r0, "listings"."listing_category_id" AS t0_r1, "listings"."listing_type_id" AS t0_r2, "listings"."user_id" AS t0_r3, "listings"."boro_id" AS t0_r4, "listings"."neighborhood_id" AS t0_r5, "listings"."building_id" AS t0_r6, "listings"."term_id" AS t0_r7, "listings"."headline" AS t0_r8, "listings"."property_type_id" AS t0_r9, "listings"."unit_num" AS t0_r10, "listings"."common_charges" AS t0_r11, "listings"."taxes" AS t0_r12, "listings"."maintenance" AS t0_r13, "listings"."num_bedrooms" AS t0_r14, "listings"."num_bathrooms" AS t0_r15, "listings"."square_footage" AS t0_r16, "listings"."list_square_footage" AS t0_r17, "listings"."description" AS t0_r18, "listings"."condition_id" AS t0_r19, "listings"."pet_policy_id" AS t0_r20, "listings"."exposure_id" AS t0_r21, "listings"."floor_plan" AS t0_r22, "listings"."photo" AS t0_r23, "listings"."security" AS t0_r24, "listings"."coop_deductible" AS t0_r25, "listings"."flip_tax" AS t0_r26, "listings"."house_families" AS t0_r27, "listings"."house_extensions" AS t0_r28, "listings"."house_stories" AS t0_r29, "listings"."house_units" AS t0_r30, "listings"."created_at" AS t0_r31, "listings"."updated_at" AS t0_r32, "listings"."price" AS t0_r33, "neighborhoods"."id" AS t1_r0, "neighborhoods"."name" AS t1_r1, "neighborhoods"."created_at" AS t1_r2, "neighborhoods"."updated_at" AS t1_r3, "neighborhoods"."boro_id" AS t1_r4 FROM "listings" LEFT OUTER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
I also tried and got the following error:
0> listings = Listing.joins(:neighborhood).where("headline like :keywords or neighborhood.name like :keywords", :keywords => "%#{keywords}%")
Listing Load (2.7ms) SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
ActiveRecord::StatementInvalid: PG::Error: ERROR: missing FROM-clause entry for table "neighborhood"
LINE 1: ..._id" WHERE (headline like '%prospect heights%' or neighborho...
^
: SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
Thoughts?
Thanks.
Edit 1:
A listing belongs_to neighborhood, and neighborhood has_many listings.
Edit 2:
Extracted one of the SQL statements and ran it via psql, this is the result:
SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%');
ERROR: missing FROM-clause entry for table "neighborhood"
LINE 1: ..._id" WHERE (headline like '%prospect heights%' or neighborho...
Try the below:
listings = Listing.includes(:neighborhood).where("listings.headline like :keywords or neighborhoods.name like :keywords", :keywords => "%#{keywords}%")
I think it should call listings.headline instead of headline
SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods"
ON "neighborhoods"."id" = "listings"."neighborhood_id"
WHERE (
headline like '%prospect heights%' #I mean here
or neighborhood.name like '%prospect heights%') #also here, should be neighborhoods.name
EDIT - do you know that there are some difference between SQLite and PostgreSQL? I faced a similar problem. I think you want ILIKE instead of LIKE:
listings = Listing.includes(:neighborhood).where("listings.headline ilike :keywords or neighborhoods.name ilike :keywords", :keywords => "%#{keywords}%")
Reformatted query:
SELECT "listings"."id" AS t0_r0,
"listings"."listing_category_id" AS t0_r1,
"listings"."listing_type_id" AS t0_r2,
"listings"."user_id" AS t0_r3,
"listings"."boro_id" AS t0_r4,
"listings"."neighborhood_id" AS t0_r5,
"listings"."building_id" AS t0_r6,
"listings"."term_id" AS t0_r7,
"listings"."headline" AS t0_r8,
"listings"."property_type_id" AS t0_r9,
"listings"."unit_num" AS t0_r10,
"listings"."common_charges" AS t0_r11,
"listings"."taxes" AS t0_r12,
"listings"."maintenance" AS t0_r13,
"listings"."num_bedrooms" AS t0_r14,
"listings"."num_bathrooms" AS t0_r15,
"listings"."square_footage" AS t0_r16,
"listings"."list_square_footage" AS t0_r17,
"listings"."description" AS t0_r18,
"listings"."condition_id" AS t0_r19,
"listings"."pet_policy_id" AS t0_r20,
"listings"."exposure_id" AS t0_r21,
"listings"."floor_plan" AS t0_r22,
"listings"."photo" AS t0_r23,
"listings"."security" AS t0_r24,
"listings"."coop_deductible" AS t0_r25,
"listings"."flip_tax" AS t0_r26,
"listings"."house_families" AS t0_r27,
"listings"."house_extensions" AS t0_r28,
"listings"."house_stories" AS t0_r29,
"listings"."house_units" AS t0_r30,
"listings"."created_at" AS t0_r31,
"listings"."updated_at" AS t0_r32,
"listings"."price" AS t0_r33,
"neighborhoods"."id" AS t1_r0,
"neighborhoods"."name" AS t1_r1,
"neighborhoods"."created_at" AS t1_r2,
"neighborhoods"."updated_at" AS t1_r3,
"neighborhoods"."boro_id" AS t1_r4
FROM "listings"
LEFT OUTER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id"
WHERE (headline LIKE '%prospect heights%'
OR neighborhood.name LIKE '%prospect heights%');
Problem seems obvious. You include table "neighborhoods", but later on are trying to use "neighborhood".
How to fix that in RoR - no idea, but perhaps it will be enough to point you in right direction.
The best way to diagnose this is to make sure your development and production environments match as closely as possible, and actually run PostgreSQL locally. Inevitably, you will encounter inconsistencies between SQLite and PostgreSQL that are difficult to debug, or will surprise you when you push your code.
If you're on a Mac, Heroku Postgres has a standalone application you can download and use with no configuration: http://postgresapp.com/