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.
Related
I have the following problem - I am coding an e-commerce website, that has promotions for a certain period of time. When time elapses promotion changes its corresponding database active value to 0. When I check for promotions the first condition is that active=1, but at some cases MySQL is ignoring it.
Here is an example of my most recent problem:
$productPromotion = $db->getResults('*', TABLE_PROMO, "active = '1'
AND (discount_subject = 'all_orders'
OR discount_subject_product = ".$values['product']['id'].")
OR (discount_subject = 'category'
AND discount_subject_category = ".$categoryId[0] . ") ORDER BY id ASC");
$db->getResult is a custom function that takes 3 parameters - What, Table and Where.
The problem is that it is returning promotions that are already expired and have active=0. Where is the problem with my sql?
You have to add brackets arround or
$productPromotion = $db->getResults('*', TABLE_PROMO, "active = '1'
AND
((discount_subject = 'all_orders' OR discount_subject_product = ".$values['product']['id'].")
OR (discount_subject = 'category' AND discount_subject_category = ".$categoryId[0] . ")) ORDER BY id ASC");
Also learn about prepared Statements to prevent SQL-injection
In my Rails 3.2 app, there is an AttendedUniversity model with the following fields:
university_name
major_name
university_type # 0 for undergrad and 1 for graduate
advisor_id # for the Advisor model
The Advisor model has many attended_universities and an attended_university belongs to an advisor.
I want to fetch advisors who had a certain undergrad and a certain graduate e.g. major1 for undergrad and major2 for graduate.
I've tried the following:
Advisor.includes(:attended_universities).
where("attended_universities.university_name = 'university1' AND
attended_universities.major_name = 'major1' AND
attended_universities.university_type = 0").
where("attended_universities.university_name = 'university2' AND
attended_universities.major_name = 'major2' AND
attended_universities.university_type = 1")
The above is giving me the empty results when it shouldn't.
What you're matching with your query is that each university in the row should have the name 'university1' AND the name 'university2'.
Similar checks are being performed for other conditions. Since no one university row in your table will have two names, two majors, or two types, you will be served with 0 results.
This is going to be a bit more complicated then you might have initially thought, if you want to do it the optimized way.
You would need to join the universities table twice to your advisors table and then apply the conditions separately to each joined table. The following should work:
joins_str = ->(tn) do
"INNER JOIN attended_universities AS #{tn} ON #{tn}.advisor_id = advisors.id"
end
criteria_one = {name: 'university1',
major_name: 'major1',
university_type: 0}
criteria_two = {name: 'university2',
major_name: 'major2',
university_type: 1}
Advisor.
joins(joins_str.call('au_one')).
joins(joins_str.call('au_two')).
where(au_one: criteria_one).
where(au_two: criteria_two).
group("advisors.id")
I would go with the top method.
You could also do it the unoptimized way, perhaps making the code a bit more readable:
advisors_with_unis = Advisor.joins(:attended_universities)
# fetch advisor ids that fulfill criteria_one
ids_one = advisors_with_unis.
where(attended_universities: criteria_one).
pluck("DISTINCT advisors.id")
# fetch advisor ids that fulfill criteria_two
ids_two = advisors_with_unis.
where(attended_universities: criteria_two).
pluck("DISTINCT advisors.id")
# get intersection of both id arrays
ids = ids_one & ids_two
# get advisors
Advisor.where(id: ids)
Yet another solution would be to fetch all records from the DB and then use Ruby for filtering:
def university_matches?(uni, name, major, uni_type)
uni.name == name &&
uni.major_name == major &&
uni.university_type == uni_type
end
Advisor.includes(:attended_universities).select do |advisor|
universities = advisor.attended_universities
universities.any? do |uni|
university_matches?(uni, 'university1', 'major1', 0)
end &&
universities.any? do |uni|
university_matches?(uni, 'university2', 'major2', 1)
end
end
As mentioned before, I'd go with the first one.
I have a somewhat complex mySQL query I am trying to execute. I have two parameters: facility and isEnabled. Facility can have a value of "ALL" or be specific ID. isEnabled can have value of "ALL" or be 0/1.
My issue is that I need to come up with logic that can handle the following scenarios:
1) Facility = ALL AND isEnabled = ALL
2) Facility = ALL AND isEnabled = value
3) Facility = someID AND isEnabled = ALL
4) Facility = someID AND isEnabled = value
The problem is that I have several nested IF statements:
IF (Facility = 'ALL') THEN
IF (isEnabled = 'ALL') THEN
SELECT * FROM myTable
ELSE
SELECT * FROM myTable
WHERE isEnabled = value
END IF;
ELSE
IF (isEnabled = 'ALL') THEN
SELECT * FROM myTable
WHERE facility = someID
ELSE
SELECT * FROM myTable
WHERE facility = someID AND isEnabled = value
END IF;
END IF;
I would like to be able to combine the logic in the WHERE clause using either a CASE statement or Conditional's (AND/OR) but I am having trouble wrapping my head around it this morning. Currently the query is not performing as it is expected to be.
Any insight would be helpful!
Thanks
You could do this...
SELECT
*
FROM
myTable
WHERE
1=1
AND (facility = someID OR Facility = 'ALL')
AND (isEnabled = value OR isEnabled = 'ALL')
However, this yields a poor execution plan - it's trying to find one size fits all, but each combination of parameters can have different plans depending on data, indexes, etc.
This means that it is better to build the query dynamically
SELECT
*
FROM
myTable
WHERE
1=1
AND facility = someID -- Only include this line if : Facility = 'ALL'
AND isEnabled = value -- Only include this line if : isEnabled = 'ALL'
I know it can feel dirty to use dynamic queries, but this is a good corner case as to when then really can excel. I'll go find a spectacularly informative link for you now. (It's a lot to read, but it's very worth learning from)
Link : Dynamic Search
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
I'm trying to select orders that have either over or under 2000 products ordered in them, depending on other values. I need to select the information from the Orders table, but check this value in the OrdersProducts table, specifically the sum of OrdersProducts.ProductQty. I also need to do this using predicate builder, because of other requirements. So far, I have this, but it isn't returning the results correctly. Its using nested Lambda expressions, which I didn't know I could do but I tried it and it works, but its not returning correct results.
Dim getOrders = From d In db.Orders _
Where d.Status = OrderStatus.Approved _
Select d
' Then a for loop adding parameters via Predicatebuilder...
If over2000 = True Then
' over 2000
predicate1 = predicate1.And(Function(d) (d.OrderProducts.Sum(Function(c) c.ProductQty > 2000)))
Else
' under 2000
predicate1 = predicate1.And(Function(d) (d.OrderProducts.Sum(Function(c) c.ProductQty < 2000)))
End If
basePredicate = basePredicate.Or(predicate1)
' End For loop
getOrders = getOrders.Where(basePredicate)
I removed some code for brevity but I think that gets the point across. How can I do this?? Thanks!
Try changing this:
(d.OrderProducts.Sum(Function(c) c.ProductQty > 2000))
to this:
(d.OrderProducts.Sum(Function(c) c.ProductQty) > 2000)
I haven't built this to test it, but it appears that it was currently trying to sum the results of a boolean comparison instead of summing the quantities and then comparing.