Trying to write rake task that contains a query that will group by one value on a join table and then sum another column. I'd like to do it using the query interface. Purpose of this task is to find the videos that have been the most popular over the last 5 days.
In pertinent part:
course_ids = Course.where(school_id: priority_schools).pluck(:id)
sections = Section.where(course_id: course_ids)
sections.each do |section|
users = section.users.select {|user| user.time_watched > 0}
user_ids = []
users.each { |user| user_ids << user.id }
user_videos = UserVideo.group(:video_id).
select(:id, :video_id, :time_watched).
where("created_at > ?", Date.today - 5.days).
where(user_id: user_ids).sum(:time_watched)
p "user_videos: #{user_videos.inspect}"
end
Any suggestions for the how / the best way to write this query?
Related
I want to filter the products on the basis of price, brand, and category. But none of them is required. So the user can filter the products on the basis of only price or price and brand. How to write this in mySQL using queries only. I have searched a lot but found the solution with the help of Stored Procedures.
If I write this query select * from product where brandid = 1 AND price = 10 and categoryid = 5. it will fetch 2 products which satisfy the where clause.
But if user doesn't want to filter the product on the basis of brand (lets say), then what will b the query? select * from product where brandid = null AND price = 10 and categoryid = 5... this will not work as I dont want to search products with brandid null. What I want is to remove that particular clause from where condition. So what my expected query is select * from product where price = 10 and categoryid = 5
Construct the query incrementally. Here it is in Ruby (since you didn't tag a programming language), but the logic is quite language-independent.
query = "SELECT * FROM products"
filters = []
params = []
if price_min
filters << "price >= ?"
params << price_min
end
if price_max
filters << "price <= ?"
params << price_max
end
if brand
filters << "brand = ?"
params << brand
end
# ...
unless filters.empty?
query += " WHERE " + filters.join(' AND ')
end
Here's what I'm trying to do. I'm trying to select from a forum views table all of the user_ids where there are 5 or more records. That's fairly easy (this is Zend):
$objCountSelect = $db->select()
->from(array('v' =>'tbl_forum_views'), 'COUNT(*) AS count')
->where('u.id = v.user_id')
->having('COUNT(user_id) >= ?', 5)
;
But I need to somehow connect this to my users table. I don't want to return a result if the count is greater than 5. I tried this:
$objSelect = $db->select()
->from(array('u' => 'tbl_users'), array(
'id as u_id',
'count' => new Zend_Db_Expr('(' . $objCountSelect . ')'),
))
;
But that returns a record for every user, leaving blank the count if it's less than or equal to 5. How do I exclude the rows where the count is less than or equal to 5?
I figured it out, but wanted to post the answer in case someone else had the same issue. I added:
->having('count > 0')
to the second select and now it works.
I have the following code and would like to convert the request into a mysql query. Right now I achieve the desired result using a manual .select (array method) on the data. This should be possibile with a single query (correct me if I am wrong).
Current code:
def self.active_companies(zip_code = nil)
if !zip_code
query = Company.locatable.not_deleted
else
query = Company.locatable.not_deleted.where("zip_code = ?", zip_code)
end
query.select do |company|
company.company_active?
end
end
# Check if the company can be considered as active
def company_active?(min_orders = 5, last_order_days_ago = 15)
if orders.count >= min_orders &&
orders.last.created_at >= last_order_days_ago.days.ago &&
active
return true
else
return false
end
end
Explanation:
I want to find out which companies are active. We have a company model and an orders model.
Data:
Company:
active
orders (associated orders)
Orders:
created_at
I don't know if it is possible to make the company_active? predicate a single SQL query, but I can offer an alternative:
If you do:
query = Company.locatable.not_deleted.includes(:orders)
All of the relevant orders will be loaded into the memory for future processing.
This will eliminate all the queries except for 2:
One to get the companies, and one to get all their associated orders.
I am using the ruby mysql2 gem to work with a database. I want to list all the countries per region in one table, then add up the hits per region.
Normally I would use the mysql SUM function, but the gem returns the headers so this is not possible.
Instead, I am getting the hit count for each country per region and add it up.
The gem returns 1 array per result and I need to get that result and add it to a running total per region.
This is my code:
#!/usr/bin/ruby -w
# simple.rb - simple MySQL script using Ruby MySQL module
require "rubygems"
require "mysql2"
client = Mysql2::Client.new(:host => "localhost", :username => "root", :database => "cbscdn")
regions = client.query("select Id from Regions")
regions.each(:as => :array) do |rid|
hit = client.query("select hits from Countries where Regions_Id='#{rid}'")
hit.each(:as => :array) do |a|
a.map do |x|
x.to_i
end
end
end
How can I implement the running count per region?
Let the database do the work:
client.query(%q{
select regions_id, sum(hits)
from countries
group by regions_id
}).each(:as => :array) do |row|
region_id, total_hits = row
#...
end
And if you want sums for regions that aren't in the countries table:
client.query(%q{
select r.id, coalesce(sum(c.hits), 0)
from regions r
left outer join countries c
on r.id = c.regions_id
group by r.id
}).each(:as => :array) do |row|
region_id, total_hits = row
#...
end
If for any reason you don't want to delegate it to the database engine, you can do this way:
(for simplicity, I assume your results are already arrays)
regions_count = regions.inject({}) do |hash, rid|
hit = client.query("whatever")
hash[rid] = hit.map(&:to_i).inject(0, :+)
hash
end
How can i fetch all the table name and row count for the specific table from the specific database ?
Result
Table Name , Row Count , Table Size(MB)
---------------------------------------
table_1 , 10 , 2.45
table_2 , 20 , 4.00
ActiveRecord::Base.connection.tables.each do |table|
h = ActiveRecord::Base.connection.execute("SHOW TABLE STATUS LIKE '#{table}'").fetch_hash
puts "#{h['Name']} has #{h['Rows']} rows with size: #{h['Data_length']}"
end
The question is tagged mysql but you can do it in a DB-agnostic manner via ORM.
class DatabaseReport
def entry_counts
table_model_names.map do |model_name|
entity = model_name.constantize rescue nil
next if entity.nil?
{ entity.to_s => entity.count }
end.compact
end
private
def table_model_names
ActiveRecord::Base.connection.tables.map(&:singularize).map(&:camelize)
end
end
Note that this will skip tables for which you don't have an object mapping such as meta tables like ar_internal_metadata or schema_migrations. It also cannot infer scoped models (but could be extended to do so). E.g. with Delayed::Job I do this:
def table_model_names
ActiveRecord::Base.connection.tables.map(&:singularize).map(&:camelize) + ["Delayed::Job"]
end
I came up with my own version which is also db agnostic.
As it uses the descendants directly it also handles any tables where the table_name is different to the model name.
The rescue nil exists for cases when you have the class that inherits from ActiveRecord but for some reason don't have a table associated with it. It does give data for STI classes and the parent class.
my_models = ActiveRecord::Base.descendants
results = my_models.inject({}) do |result, model|
result[model.name] = model.count rescue nil
result
end
#temp_table = []
ActiveRecord::Base.connection.tables.each do |table|
count = ActiveRecord::Base.connection.execute("SELECT COUNT(*) as count FROM #{table}").fetch_hash['count']
size = ActiveRecord::Base.connection.execute("SHOW TABLE STATUS LIKE '#{table}'").fetch_hash
#temp_table << {:table_name => table,
:records => count.to_i,
:size_of_table => ((BigDecimal(size['Data_length']) + BigDecimal(size['Index_length']))/1024/1024).round(2)
}
end
end