Complex Left Outer Self-Join in Rails 5 - join

I have a list_events table where I want to get the latest event per user per list between a certain time. Here's an example of the table.
id user_id list_id event created_at
1 5 1 sub 13:45
2 1 1 sub 14:01
3 1 2 sub 14:02
4 3 1 sub 14:03
5 4 1 sub 14:04
6 1 1 unsub 14:05
The last events per user for list 1 between 14:00 and 15:00 would be...
id user_id list_id event created_at
4 3 1 sub 14:03
5 4 1 sub 14:04
6 1 1 unsub 14:05
In my Rails 5 model I've written the query like so:
list.events
.joins("
left outer join list_events b
on list_events.user_id = b.user_id
and list_events.list_id = b.list_id
and list_events.created_at < b.created_at
")
.where("b.user_id is null")
.where(created_at: start..end)
This works fine, but I'm wondering if there's a way to write this without hand-coding the join. I do notice Rails has a left_outer_join method, but there's no way to specify custom on. Perhaps with a belongs_to?
Also if there's a way to alias list_events as a while still being able to take advantage of the list.events relationship abstraction.

Related

Bigquery - LEFT Join two queries with second using most recent dates

I have a query that does two queries. The first query looks at future events and what user is in that event from one table. The second query looks at the historical events and creates stats for the user in that event based off all the previous events that user was in from a second table.
I am having troubles joining the two queries. The goal of the join would be to join the second query to the first query based off the most recent stats for each user. The stat can't be newer than the date that the future event occured on. If that user hasn't been in any previous event it would just return null for the query 2 in the join.
Below is also an example table for the future query, a table for the historic query, and an ideal output of the join.
Future:
user
date
event code
event info
User 1
1/26/2023
5596
info_5596
User 2
1/26/2023
5586
info_5586
User 3
1/26/2023
5582
info_5582
User 1
1/20/2023
5492
info_5492
User 1
1/2/2023
5341
info_5341
User 2
1/2/2023
5333
info_5333
Historical:
user
date
stat 1
stat2
event code
event info
User 1
1/25/2023
10
52
4352
info_4352
User 2
1/25/2023
11
22
4332
info_4332
User 2
1/12/2023
2
45
4298
info_4298
User 3
1/12/2023
8
88
4111
info_4111
User 1
1/12/2023
7
67
4050
info_4050
User 3
1/2/2023
3
91
4000
info_4000
User 1
1/1/2023
6
15
3558
info_3558
Output of the JOIN:
user
date future
stat 1
stat2
event code future
event info future
User 1
1/26/2023
10
52
5596
info_5596
User 2
1/26/2023
11
22
5586
info_5586
User 3
1/26/2023
8
88
5582
info_5582
User 1
1/20/2023
7
67
5492
info_5492
User 1
1/2/2023
3
91
5341
info_5341
User 2
1/2/2023
null
null
5333
info_5333
I tried using a subquery in the join, but bigquery was saying that it is unsupported. Below is my code attempt. I have also tried using MAX() but it was not liking that to be used in the join as well
Another option I am thinking of is to join the two datasets before ever calculating the query 2 stats. Then filtering. I have a large query already written for both though, so I would prefer not to start over.
Select Distict
A.*
B.Stat1, B.Stat2
from future as A
Left Join historic as B
ON (
A.Date = (Select MAX(B.date) FROM historic as recent_historic WHERE recent_historic.user = A.user)
AND
A.user = B.user
)
ORDER BY A.date

Sort a table using the total of other tables

2 tables: memberships and week_scores
Membership has_many :week_scores
WeekScore belongs_to :membership
Every membership has 16 week_scores
Every week_score table has a score column that has an integer from 0 to 20.
So just to be clear all memberships have 16 week_scores and I want to display a leaderboard table of all members of the group sorted by the total score of all their 16 week_scores tables.
It should look something like this
Username | Score
David | 114
Rick | 97
Mike | 95
...
The score column should be a sum of all the week_scores one user has so in case of David it was
week_score.score 1: 15
week_score.score 2: 12
week_score.score 3: 14
...
week_score.score 16: 9
total: 114
If the name of the post is not good let me know.
One way of doing this is with an subselect:
subselect = "SELECT SUM(score) FROM week_scores WHERE membership_id=memberships.id"
#memberships = Membership.
select("memberships.*, (#{subselect}) AS total_score").
order("total_score DESC")
The additional column specified in the select clause (total_score) will be available on the returned membership instances as if it were a “real” attribute, so calling something like #memberships.first.total_score will work.
(Note that I extracted the subselect into a separate variable only to make the code more readable, it is of course also possible to have it inline instead.)

How can i count a column doing a condition?

i'm trying to count a column using conditions
Tables
select* from policies
|policies|
|id| |client_id| |expiration_date
1 1 2013-10-10
2 1 2013-10-10
3 2 2013-10-10
|clients|
|id| |name|
1 ABC
2 CDE
3 EFG
i WANT
select *,count(number_expirations) from policies where(client=1)
select *,count(number_expirations) from policies where(client=2)
|policies|
|id| |client_id| |number_of_expirations|
1 1 2
3 2 1
This is consult
#count = Policy.count('expiration_date',:joins=> :client,:conditions=>['name =?',params[:name])
But i'm trying to count expiration_date by client_id
I will really appreciate help.
i did not completely understand your question or the finder that you provided, but i think that you want to get a count of expiration_dates grouped by client.
this would look like this:
Policy.where(name: params[:name]).group(:client_id).count

Active Record query for latest update record

I got a small problem that can't be fix now by me ^_^ and would like to see your advice.
The problem is only about getting the latest updated of several record from mysql table.
Here is the sample data but the real one has thousands of record.
Id site_id updated_at value
1 1 "2012-05-14 09:04:02" 5
2 2 "2012-05-14 09:04:02" 8
3 2 "2012-05-15 09:04:02" 9
4 3 "2012-05-16 09:04:02" 7
5 1 "2012-05-17 09:04:02" 5
6 1 "2012-05-18 09:04:02" 3
and I want to have XXXX function that will return
Id site_id updated_at value
6 1 "2012-05-18 09:04:02" 3
4 3 "2012-05-16 09:04:02" 7
3 2 "2012-05-15 09:04:02" 9
Something like this?
YourModel.order("updated_at desc").limit(3)
Assuming your model is called Account:
Account.joins("LEFT OUTER JOIN accounts a
ON a.site_id = accounts.site_id AND a.value > accounts.value").
where("a.value IS NULL").order("accounts.updated_at DESC").limit(3)

How to store word compositions in a relational database in a normalized way?

I'm trying to find a nice way to store word compositions of the following form:
exhaustcleaningsystem
exhaust cleaning system
exhaustcleaning system
exhaust cleaningsystem
The combinations are given by a default per case. Every word in a composition is stored as a unique row in table 'labels'.
labels
id value
--------------------------
1 exhaustcleaningsystem
2 exhaust
3 cleaning
4 system
5 exhaustcleaning
6 cleaningsystem
I thought about a new table called 'compositions':
compositions
id domain_id range
----------------------
1 1 2,3,4
2 1 5,4
etc...
But storing multiple separated values in a column isn't normalized design. Any ideas for that?
BTW: I'm using MySQL und ActiveRecord/Rails.
The design you propose is not even in first normal form, since range is not atomic
The schema I'd use here would be
compositions
id domain_id
-------------
1 1
2 1
compositions-content
composition_id rank label_id
------------------------------------------
1 1 2
1 2 3
1 3 4
2 1 5
2 2 4
with composition_id referencing an composition.id and label_id referencing label.id
The rank column is optional and should be here if and only if the range you define here is order-sensitive.
With this design, you have some referential integrity at DB level.
Well, this is as far as I can think of in terms of normalisation:
sets
id domain_id
--------------
1 1
2 1
etc...
compositions
id set_id label_id order
---------------------------
1 1 2 1
2 1 3 2
3 1 4 3
4 2 5 1
5 2 4 2
etc...

Resources