How to convert SQL into activerecord Rails - sql

My sql that gives me the results I want (a single record from books table).
select *
from books
where book_number = ? and id not in (select book_id from checkout_logs where returned_date is null) limit 1
my best active record attempt:
#book = Book.where(book_number: params[:book_number]).where.not(id: CheckoutLog.where(returned_date: nil)).find(1)
? is the params[:book_number]
here is my Rails schema.rb
ActiveRecord::Schema.define(version: 2020_12_30_171415) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "books", force: :cascade do |t|
t.string "title"
t.string "author"
t.string "genre"
t.string "subgenre"
t.integer "pages"
t.string "publisher"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "book_number"
end
create_table "checkout_logs", force: :cascade do |t|
t.datetime "checkout_date"
t.datetime "due_date"
t.datetime "returned_date"
t.bigint "user_id", null: false
t.bigint "book_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["book_id"], name: "index_checkout_logs_on_book_id"
t.index ["user_id"], name: "index_checkout_logs_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.boolean "admin", default: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "checkout_logs", "books"
add_foreign_key "checkout_logs", "users"
end
The idea is I want to get a single book (id) from the book table with a given book_number (think ISBN) that doesn't have a record in checkout_log table with a matching book_id and a null for returned_date. (indicating a book is checked out and not available.)
EDIT: changed the last part from .take to .find(1) which now does take 1 record, problem is the
.where.not(id: CheckoutLog.where(returned_date: nil))
part isn't filtering out the books which have a checkout_log record and no returned_date.

.where.not(id: CheckoutLog.where(returned_date: nil))
=> I think you are filtering wrong id, you need to filter by book's id, not id of CheckoutLog.
So your query should be:
.where.not(id: CheckoutLog.where(returned_date: nil).pluck(:book_id))

Use of the includes query method may help.
Book.includes(:checkout_logs).where("books.book_number=? AND checkout_logs.returned_date IS NOT NULL", params[:book_number]).limit(1)
More info on query methods is in the api

Related

Total value of warehouse active record

I have the object Tool and I need to calculate the cost of all the Tools in the warehouse.
I can sum the column price Tool.sum(:price) but i need to sum (quantity * price) of all the table Tools.
Thanks in advance.
create_table "tools", force: :cascade do |t|
t.integer "quantity"
t.string "name"
t.integer "price"
t.boolean "avilable"
t.string "store"
t.string "comments"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
Check out the example listed for ActiveRecord::Calculations#calculate.
You can do the following:
Tool.sum("quantity * price")

How to import data into rails models from a table in a database

I have the following schema in my rails app:
ActiveRecord::Schema.define(version: 20161020060112) do
create_table "authors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "languages", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "quotes", force: :cascade do |t|
t.string "title"
t.date "date"
t.string "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "author_id"
t.integer "language_id"
t.integer "source_id"
t.index ["author_id"], name: "index_quotes_on_author_id"
t.index ["language_id"], name: "index_quotes_on_language_id"
t.index ["source_id"], name: "index_quotes_on_source_id"
end
create_table "source_types", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sources", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "source_type_id"
t.index ["source_type_id"], name: "index_sources_on_source_type_id"
end
end
And this is the schema of the table that I want to import data from:
CREATE TABLE quotation_data(
id integer primary key,
author_name text,
source_type text,
source text,
language text,
date text,
title text,
body text
);
I did find some posts on Stackoverflow but none that tell about importing data from a single table into fields distributed over many models.
Thanks!
I have imported data from database I create the list that list everything that I have inserted in the database.
like:
Under controller#show
def show
#quotes = Quote.all
end
views#show
<ul>
<% #quotes.each do |quote| %>
<li>
<%= link_to quotes.title, edit_quotes_path(quote) %>
</li>
<% end %>
</ul>
This will help you to view what is on the database and edit it also.

SQL Query in two different Rails models that have no association

I have a landlords table, and landlord_addresses table, and a landlord_companies table. From the landlords index view and using the landlords_controller I need to be able to search the landlord_companies, BUT landlords and landlord_companies have no ActiveRecord association with each other. Obviously I can't use what I've written below, but I am not sure how to search the landlord_companies ...any help would be great!
#landlord = #landlords.includes(:landlord_company)
.references(:landlord_companies)
.where("landlord_companies.llc_name LIKE ?", "%#{params[:landlord_llc_search]}%")
Schema:
create_table "landlords", force: :cascade do |t|
t.string "name"
t.string "contact_name"
t.string "contact_number"
t.integer "listing_agent_id"
t.boolean "own_application"
t.boolean "own_credit"
t.boolean "accepts_roommate_matchups"
t.boolean "management_company"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "landlord_companies", force: :cascade do |t|
t.string "llc_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "landlord_address_id"
end
I was able to figure this out and actually use the SQL query I had already written. I thought there was no association, but technically there was using a through.
Landlord.rb
has_many :landlord_companies, through: :landlord_addresses

Most efficient way to get a Users Tags

I have a somewhat complicated database structure in a Ruby on Rails project. A User can have many EmailAccounts, an EmailAccount can have many Subscriptions (many-to-many via a join table), and a Subscription can have many Tags (also many-to-many)
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.date "dob"
t.string "gender"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "email_accounts", force: :cascade do |t|
t.integer "user_id"
t.string "email"
t.string "provider"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "account_subscriptions", force: :cascade do |t|
t.integer "email_account_id", null: false
t.integer "subscription_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "subscriptions", force: :cascade do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "domain"
end
create_table "sub_tags", force: :cascade do |t|
t.integer "subscription_id", null: false
t.integer "tag_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
I want to see which Tags apply to a User through this relationship. Whats the most efficient way to do so (eager load all the relationships and loop through them, do a multi-level join in Postgres, etc)?
Assuming you have the right models
user = User.find(user_id)
tags = Tag.where(:id => SubTag.where(:subscription_id => AccountSubscription.where(:email_account_id => user.email_accounts.map{|e| e.id}).map{|a_s| a_s.subscription_id}).map{|s_t| s_t.tag_id})
tag_name = tags.map{|t| t.name}
this should work fine given you have the right indexes.

How to order my resource by column count in SQL or scope?

I have 2 models, photos and albums.
Photos has many albums.
Albums has many photos.
I have a join table called Photo Listings.
I want to order my photos by most albums to less, but I don't get it to work!
#most_albums_photos = #photos.joins(:photo_listings).order('COUNT(albums.id)')
I get the following error:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "albums"
LINE 1: SELECT DISTINCT "photos".id, COUNT(albums.id) AS alias_0 FR...
Any SQL man in the house? Thanks
Schema
create_table "photo_listings", force: true do |t|
t.integer "album_id"
t.integer "photo_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "albums", force: true do |t|
t.string "title"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.text "description"
t.integer "cached_votes_total", default: 0
t.integer "cached_votes_score", default: 0
t.integer "cached_votes_up", default: 0
t.integer "cached_votes_down", default: 0
t.integer "cached_weighted_score", default: 0
t.integer "cached_weighted_total", default: 0
t.float "cached_weighted_average", default: 0.0
t.boolean "selected", default: false
end
create_table "photos", force: true do |t|
t.string "title"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.string "photo"
t.integer "user_id"
t.integer "category_id"
t.integer "zone_id"
t.integer "pins_count", default: 0
t.integer "cached_votes_total", default: 0
t.integer "cached_votes_score", default: 0
t.integer "cached_votes_up", default: 0
t.integer "cached_votes_down", default: 0
t.integer "cached_weighted_score", default: 0
t.boolean "terms"
t.string "slug"
t.boolean "sponsored"
t.integer "order"
t.string "url"
t.boolean "editor_pick", default: false
t.tsvector "tsv"
t.boolean "flash"
t.boolean "flash_active"
end
#MrYoshji was almost there. Finally, I got it working with:
#most_albums_photos = #photos.joins(photo_listings: :album).group("photos.id").order('COUNT(albums.id) DESC')