Rails intersect query with joint table - mysql

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.

Related

join fields with numbers

Some books have more than one author, I have a table with with book_id and author_id1, author_id2, author_id3, and author_id4. I have a table with author_id and author_name.
How can I join these two tables and the main table with book_id to get the authors names together in a data row from a sql query join.
Example:
SELECT book.book_id, book.title, author.author, book.location
FROM books AS b JOIN book_authors AS ba ON b.book_id = ba.book_id JOIN authors AS a ON REGEX ba.authors_id$ = a.authors_id
Not sure about REGEX ($) use in sql Should display id, title, authors, location
How do I get all authors_id# to match authors_id ( notice one has number at end other does not)?
update: So, I would like to get book_authors.authors_id1 to match authors.authors_id, book_authors.authors_id2 to match authors.authors_id, book_authors.authors_id3 to match authors.authors_id, book_authors.authors_id4 to match authors.authors_id and return all the matching authors in list.
...
# merge book_authors and authors into one dataframe
ba_df.rename(columns= {'authors_id1': 'authors_id'}, inplace=True)
ba_df['authors_id'] = ba_df['authors_id'].map(a_df.set_index('authors_id')['authors_name'])
ba_df.rename(columns = {'authors_id':'authors_name1', 'authors_id2': 'authors_id'}, inplace = True)
ba_df['authors_id'] = ba_df['authors_id'].map(a_df.set_index('authors_id')['authors_name'])
ba_df.rename(columns = {'authors_id':'authors_name2', 'authors_id3': 'authors_id'}, inplace = True)
ba_df['authors_id'] = ba_df['authors_id'].map(a_df.set_index('authors_id')['authors_name'])
ba_df.rename(columns = {'authors_id':'authors_name3', 'authors_id4': 'authors_id'}, inplace = True)
ba_df['authors_id'] = ba_df['authors_id'].map(a_df.set_index('authors_id')['authors_name'])
ba_df.rename(columns = {'authors_id':'authors_name4'}, inplace = True)
...
Was working through another dataframe and got the idea to use map after rename to set_index the same on both dataframes. Now, the map lines can work, just have to rename the common column , so as not to overwrite, in this case it was authors_id, replaced with authors_name1, 2, 3 & 4, which equates to the authors_id1, 2, 3 & 4. And yes, it is not pure sql, but it works for python, which is where I had the problem.

I need to convert the code from flask_sqlalchemy ORM to raw SQL

I have two tables: User, Posts and association table - Followers.
I am creating a subscriber system in my project (Social Network) and for the sake of speed I want to write instead of this function:
def followed_posts_by_user(self):
followed_posts = Post.query.join(
followers, (followers.c.followed_id == Post.user_id)).filter(
followers.c.follower_id == self.id)
filtered = Post.query.filter_by(user_id=self.id)
return followed_posts.union(filtered).order_by(Posts.timestamp.desc())
A raw SQL because this function seems too complex, I wrote this code
comm = """
SELECT * from posts
INNER JOIN followers
ON followers.followed_id=posts.user_id
WHERE followers.follower_id = '(%s)';
"""
cursor.execute(comm, self.id)
But it doesn't work.

MySQL sql ignoring WHERE condition

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

Convert this code into active record/sql query

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.

too many fields to specify in result set of join

So similar questions have been asked with not much of an answer....
I have a Stats Table related to a Branch table. The Stats records contain pc stats of a particular bank branch.
Branch
+Code
+Name
+Area
...
Stats
+BranchCode
+IP
+UpSpeed
+DownSpeed
...
Here is my linq query...
var stats = from st in store.Stats
join b in store.Branches on st.BranchCode equals b.Brcd
select new
{
st.ID,st.IP,st.Name,b.Brcd,b.Branch_Name..............
};
The issue is st and b have a LOT of fields, for now I guess I will type them all... but isn't there a solution to this? *Prevent the typing of all fields... something like a * wildcard?
Did try intersect however the types need to be the same!
Thanks
Gideon
1
var stats =
from st in store.Stats
join b in store.Branches on st.BranchCode equals b.Brcd
select new
{
Stats = st,
Branch = b
};
Creates anonymous instances with one Stats and one Branch.
2
var stats =
from b in store.Branches
join st in store.Stats
on b.Brcd equals st.BranchCode
into branchstats
select new
{
Branch = b
Stats = branchstats
};
Creates anonymous instances with one Branch and its Stats.
3
var stats =
from b in store.Branches
select new
{
Branch = b
Stats = b.Stats
};
Same as 2, If there's an association between the two types in the designer, then there's a relational property generated on each type.
4
DataLoadOptions dlo = new DataLoadOptions()
dlo.LoadWith<Branch>(b => b.Stats);
store.LoadOptions = dlo;
List<Branch> branches = store.Branches.ToList();
Here, DataLoadOptions are specified that automatically populate the Stats property when any Branch is loaded.