Threshold summing in rails/postgres - sql

I'm making a walkathon rails app. Each walker is sponsored by people who pledge money per lap up to a maximum amount.
I have a table called sponsorships that has columns walker_id, amount_per_lap, and max_amount. I want to write a SQL query to determine how much money a walker has raised.
There is also a walkers table that has id, name, and laps columns.
I know this isn't valid SQL, but I wanted something like this, but don't know the best way to do it. The walker_id and laps could be provided before executing the query.
SELECT SUM(MIN(Laps * sponsorships.amount_per_lap, sponsorships.max_amount)) FROM sponsorships
where sponsorships.walker_id = 1;
I'm making this in rails, so I was trying to figure out how to do something like this in Arel, but couldn't figure it out.
Any help would be greatly appreciated.
Edit: clarifying the walkers table.
Edit2: Accidentally had max instead of min in the pseudo code

I think the SQL you're looking for is this:
select w.id, w.name, sum(least(w.laps * s.amount_per_lap, s.max_amount))
from walkers w
join sponsorships s on w.id = s.walker_id
group by w.id, w.name
The least function is what applies your "no more than max_amount" condition. Translating that to AR should be a simple matter for you now that you know what to SELECT; I tend to go straight to SQL for anything like this.

I would try something like:
class Sponsorship < ActiveRecord::Base
name
walker_id
amount_per_lap
max_amount
class Walker < ActiveRecord::Base
name
number_of_laps
walker_sum = 0
walker=walker.find(1) # For 1 walker.
walker.sponsorships.each do |sponsor| #
walker_sum+=
min((sponsor.amount_per_lap * walker.number_of_laps), (sponsor.max_amount))
end

Related

sql is duplicating my results

I believe the problem is within my joins but i am unable to correct it. The SQL should return 3 rows however it is duplicating and returning 12 rows instead. Any help would be much appreciated!
SELECT J.JOURNEY_NUMBER,
L.DESCRIPTION,
L.USE_CODE,
J.REAL_START_DATE,
J.REAL_END_DATE,
S.STOP_ID,
SN.WRIN_ID,
J.JOURNEY_ID
FROM PDA_STG.JOURNEY J,
PDA_STG.RESTAURANT R,
PDA_STG.LOCATION L,
PDA_STG.SERIAL_NUMBER SN,
PDA_STG.STOP S
WHERE J.JOURNEY_ID = R.JOURNEY_ID
AND l.loc_id = r.rest_loc_id
AND J.JOURNEY_ID = S.JOURNEY_ID
AND S.STOP_ID = SN.STOP_ID
AND SN.WRIN_ID = '00768669'
AND j.dc_loc_id = '994'
AND J.JOURNEY_ID = '357020'
AND J.PLANNED_START_DATE < '20-APR-17'
ORDER BY J.JOURNEY_ID DESC
You are probably joining records that you don't want to join for which you'd have to add some join criteria. (For instance if the serial number could change for a stop, i.e. you keep old serial numbers with a date, you'd only want the latest serial number, not all.)
In order to find the flaw in your query you can select * and see what records you are actually selecting.
Thanks for the feedback, i just done as India.Rocket said and it worked perfectly.
without sample it's difficult to tell what's wrong with the query. But
if rows are exact duplicate then just put a distinct after select.
That should do the job – India.Rocket 46 mins ago

Negative comparison with ActiveRecord

I have two tables:
sales
period_id
customer_id
product_id
value
total_sales
period_id
customer_id
product_id
value
I want to select every combination of period_id and customer_id that exists on sales but don't exists on total_sales. I believe there's a short way to do this. But every approach I thought involves N+1 queries.
How can I do this?
In my opinion this is precisely the case where it's reasonable to diverge from the purist ActiveRecord approach and bring some SQL to the table. This should do the trick perfectly well:
Sale.find_by_sql("
SELECT * FROM sales WHERE NOT EXISTS (
SELECT 1 FROM total_sales
WHERE total_sales.period_id = sales.period_id AND
total_sales.customer_id = sales.customer_id)
")
I suppose it also can be done in a more ActiveRecordish style, but it will most certainly lose clarity of the code.
Of course if you ever drop this to you codebase, you really should wrap it in a method and leave in the model, so that SQL code at least doesn't bleed into the controller.
Hope this helps!
P.S. Oh, here's the SQL fiddle I used to test this out: http://sqlfiddle.com/#!15/eaf1d/1
How about you first get the list that matches, then use that to get the rows that you actually want:
unwanted_ids = Sale.joins('inner join total_sales on sales.period_id = total_sales.period_id and sales.customer_id = total_sales.customer_id').pluck('distinct sales.id')
wanted_rows = unwanted_ids.any? Sale.where('id not in (?)', unwanted_ids) : Sale.all
wanted_rows.select(:period_id, :customer_id) #if you only want these two
This way you only need two queries.
EDIT:
You can also do this:
Sale.joins('left outer join total_sales on sales.period_id = total_sales.period_id and sales.customer_id = total_sales.customer_id').where('total_sales.period_id is null')
Does it in one query

Rails query - latest Post for each day

A User has_many Posts. I want to retrieve the latest Post for each day (using created_at), ignoring other posts that may have been written earlier. Another way to pose this question might to ask for a each top salary earning employee by department - same thing I think.
How do I write this query in Rails (4.0 preferably)? I think it has something to do with group and maximum but I can't seem to get it. Is there a way to do it without resorting to SQL?
To clarify, what I'd like returned is an array of post objects that are the last ones written on their respective date.
Thanks!
Something like this. You can convert this to AREL syntax as needed:
SELECT posts.created_at, *
FROM posts
INNER JOIN (
SELECT MAX(created_at) AS max_order_date FROM posts
GROUP BY DATE(posts.created_at)
) AS last_postings ON last_postings.max_order_date = posts.created_at
ORDER BY DATE(created_at) DESC
LIMIT 10
AREL syntax might be:
join_sql = <<-SQL
INNER JOIN (
SELECT MAX(created_at) AS max_order_date FROM posts
GROUP BY DATE(posts.created_at)
) AS last_postings ON last_postings.max_order_date = posts.created_at
SQL
Post.joins(join_sql).order('DATE(created_at) DESC')
Remove the LIMIT as it suits you.
It's not very clean, but this works in Rails 3 (taken from a Book model in my case) using PostgreSQL syntax for truncating the created_at to the date:
max_created_at_list = Book.select("max(created_at) as created_at").group("date_trunc('day',created_at)")
last_books = Book.where(:created_at => max_created_at_list)
... or just:
Book.where(:created_at =>Book.select("max(created_at) as created_at").group("date_trunc('day',created_at)"))
You'd want an index on created_at for large data sets, and either created_at to be constrained to not null at the database level or an "is not null" predicate if the RDBMS you use does not index nulls (eg. Oracle)
Try this
Post.select("user_id, max(created_at) as created_at").group(:user_id)

Convert a SQL query in Java Persistence Query Language (JPQL)

I'm having a little problem to convert this SQL query to JPQL:
select max(datePurchase) from purchases where userId = id and date_trunc('day',datePurchase)
in (select distinct (date_trunc('day',datePurchase)) as day from purchases where userId = id and datePurchase < initialDate and datePurchase > finalDate) group by date_trunc('day',datePurchase)
This sql is working well, that returns de last purchase per day made from a user. I tried to do the same, in JPQL:
Query query = em.createQuery("SELECT u MAX(u.datePurchase) FROM Purchases u WHERE u.userId.id = :id AND FUNC('day',u.datePurchase)" +
"IN (SELECT DISTINCT (FUNC('day',u.datePurchase)) AS day FROM Purchases WHERE u.userId.id = :id AND u.datePurchase < :finalDate AND u.datePurchase > :inicialDate) GROUP BY FUNC('day',u.datePurchase)");
query.setParameter("id", idUsuario);
query.setParameter("dataInicial", dataInicial);
query.setParameter("dataFinal", dataFinal);
List<MovSaldo> saldos = (List<MovSaldo>) query.getResultList();
em.getTransaction().commit();
The errors are:
"The IN expression does not have a valid expression." "An identification variable must be provided for a range variable declaration."
Probably is not something very difficult, but i have already spent a little frustrating time in it. Can someone please help me?
Although the answer is probably late for you I still posted it because it might help someone in the future.
In the nested select you have to put an identifier for the entity you are working with: FROM Purchases p and update things like (FUNC('day',u.datePurchase)) to (FUNC('day',p.datePurchase))
Best!

A task about friend of friend of .... on Oracle DB

There are two tables:
Table of peoples (m_id, m_name);
Table of links (m_id, f_id), where both fields link to m_id from first table
I need an Oracle database query that prints the word "Possible" if everyone is linked to everyone by not more than:
through 3 friends
through N friends
otherwise prints "Impossible"
Help me with this task if it's possible, or at least show me where to look for the answer, I mean what I have to read before, and what's necessary for solving this task.
I am not sure i got you question correct but i guess you require something like this.
select p.m_id,count(l.f_id),'Possible' col
from people p,
links l
where p.m_id = l.m_id
group by p.m_id
having count(l.f_id) >= 3
union
select p.m_id,count(l.f_id),'Impossible' col
from people p,
links l
where p.m_id = l.m_id
group by p.m_id
having count(l.f_id) < 3