Laravel model query with sum and grouping - laravel

I have a table like this:
|item_name |uom |qty |order_number
|bike |pc |1 |101
|ball |pc |2 |102
|bike |pc |5 |103
|car |pc |3 |106
|bike |pc |4 |108
|ball |pc |1 |109
|bike |pc |6 |115
|car |pc |1 |111
And I want to see in a view this data:
bike - 16 pcs overall:
101 - 1
103 - 5
108 - 4
115 - 6
ball - 3 pcs overall:
102 - 2
109 - 1
etc...
I tried wuth this query, but I think it is not correct, because it gives me only name and sum of qty:
$saps = Item::where('year', 2019)->groupBy('item_name')->selectRaw('item_name, sum(qty) as sum')->get('item_name','sum');

Item::where('year', 2019)
->select('name','order_number',\DB::raw("SUM(qty) as qty"))
->groupBy('name')
->havingRaw('COUNT(*) > 1')
->get();
Try this query.
Outupt

Try this query.
$saps = Item::where('year', 2019)->selectRaw('order_number, qty')
->groupBy('item_name')
->get();
Or
$saps = Item::where('year', 2019)->selectRaw('order_number, qty,item_name')
->get();
$saps_new = $saps->groupBy('item_name')->map(function ($row) {
return $row->sum('qty');
});

You can get the initial groups with this:
$saps = Item::where('year', 2019)->selectRaw("SUM(qty) as total, item_name, id")
->groupBy('item_name')
->get();
You can then lazy or eager load the listing for each item.
If you go with lazy loading you can map on results and then lazy load the listing based on each item
$saps->map(function($item){
$items = Item::where('year', 2019)->where('item_name', $item->item_name)
->get();
}
or eager load like this:
Item::where('year', 2019)->whereIn('item_name', $saps)
->get();

Related

Eloquent how to get Sum of a column separated by 2 has many relationships

I am trying to get the sum of a column of another table that is 2 hasMany relationships away. Ideally I would like to use eloquent and not use raw queries or joins.
Table structure
factions table
id
example rows
1
2
3
islands table
id | faction_id
example rows
1 | 1
2 | 2
3 | 3
4 | 1
5 | 2
6 | 3
shrines table
id | island_id | points (integer)
example rows
1 | 1 | 200
2 | 2 | 100
3 | 3 | 50
4 | 4 | 75
5 | 5 | 200
6 | 6 | 100
7 | 1 | 25
8 | 2 | 40
9 | 3 | 50
Relationships:
Faction hasMany Island
Island hasMany shrines
I would like to be able to get all Factions and get the sum of the points that effectively belong to each faction where the result is something like
$factions = [
['id' => 1, 'points' => 300],
['id' => 2, 'points' => 340],
['id' => 3, 'points' => 200],
...
]
so points in the sum of the shrines.points that belongs to the islands that belongs to the faction.
Any help is appreciated and I've found a few posts that talk about similar problems but the problem isn't exactly the same and the solution isn't exactly what I am looking for.
If you add a hasManyThrough relationship on your Faction model, you should be able to use an accessor to do it:
Faction.php
protected $appends = ['points'];
public function shrines(){
return $this->hasManyThrough(Shrine::class, Island::class);
}
public function getPointsAttribute(){
return $this->shrines()->sum('points');
}

How can we solve a two-sum algorithm as a big data problem leveraging mapreduce or spark?

Assuming the list/array of numbers are present in a very huge data file and we need to find the pair of sums that match a particular number 'k'. I know how to solve it typically using data structures but I am unable to think of an approach to solve it leveraging Hadoop MR or spark particularly.
Assuming a file having 1,2,3,6,7,7,8,9
My thought process:
-Considering the data into a dataframe and then adding one more column to it which identifies the difference i.e.if i<=k/2 then k-i else i. then now my dataframe for above data is like:
number
number_2
1
9
2
8
3
7
7
7
7
7
8
8
9
9
Once the df is created, I am planning to aggregate the data with key based on number_2. But I am unable to assume the aggregating logic.
Any help will be appreciated.
Imagine you have a file named numbers.txt like the following:
10
5
8
7
3
6
9
11
3
1
you can achieve your goal like this:
int desiredSum = 15;
SparkSession spark = SparkSession
.builder()
.appName("My App")
.master("local[*]")
.getOrCreate();
Dataset<Row> rdd = spark
.read()
.text("numbers")
.withColumnRenamed("value", "number")
.withColumn("number", col("number").cast(DataTypes.LongType));
rdd.createOrReplaceTempView("myTable");
spark.sql("select first.number, second.number as number_2 from myTable first inner join myTable second on first.number + second.number =" + desiredSum + " where first.number <= second.number").show();
+------+--------+
|number|number_2|
+------+--------+
| 5| 10|
| 7| 8|
| 6| 9|
+------+--------+
Or if data is small you can achieve your goal using Cartesian product in Spark like this:
int desiredSum = 15;
SparkSession spark = SparkSession
.builder()
.appName("My App")
.master("local[*]")
.getOrCreate();
Dataset<Row> rdd = spark
.read()
.text("numbers.txt")
.withColumnRenamed("value", "number")
.withColumn("number", col("number").cast(DataTypes.LongType));
Dataset<Row> joinedRdd = rdd.crossJoin(rdd.withColumnRenamed("number", "number_2")).filter("number <= number_2");
UserDefinedFunction mode = udf((UDF2<Long, Long, Object>) Long::sum, DataTypes.LongType);
joinedRdd = joinedRdd.withColumn("sum", mode.apply(col("number"), col( "number_2"))).filter("sum = " + desiredSum);
joinedRdd.show();
in which result to this:
+------+--------+---+
|number|number_2|sum|
+------+--------+---+
| 5| 10| 15|
| 7| 8| 15|
| 6| 9| 15|
+------+--------+---+
**take into account the Order of time and space complexity when you use Cross join**

Laravel: relationship between siblings

I have 4 tables flats, residents, floors & resident_floors. The relations between them is as follows:
flats:
id | name
-----------------------------
1 | Flat -1
2 | Flat -2
...
flat_residents:
id | flats_id | name
-----------------------------
1 | 1 | Resident - 1
2 | 2 | Resident - 2
3 | 3 | Resident - 3
...
flat_floors:
id | flats_id
-----------------------------
1 | 101
2 | 102
3 | 201
4 | 202
...
flat_resident_floors:
id | residents_id | floors_id
-----------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 3
4 | 3 | 4
I am trying to create the relationship between them to display the data as follows:
Flat / Floor(s) | Resident
1 / 101, 102 | Resident - 1
2 / 201 | Resident - 2
3 / 202 | Resident - 3
Resident.php
public function floors()
{
return $this->hasManyThrough(Floor::class, ResidentFloor::class, 'floors_id', 'id');
}
Here is the query which is being generated:
SELECT * FROM floors
INNER JOIN flat_resident_floors ON flat_resident_floors.id = floors.id
WHERE flat_resident_floors.floors_id = ?
Where as it should be:
SELECT * FROM floors
INNER JOIN flat_resident_floors ON flat_resident_floors.floors_id = floors.id
WHERE flat_resident_floors.residents_id = ?
I don't understand what or where am I doing wrong..?
Don't think about the underlying SQL so much, instead use the Eloquent relationships to your advantage. In this case, it seems that Flat should hasMany(Floor::class) and Floor should hasMany(Resident::class). Your Flat then hasManyThrough relationship with Resident which writes itself (without needing a pivot table as you are trying to do).
class Floor
{
public function residents()
{
return $this->hasMany(Resident::class);
}
public function flat()
{
return $this->belongsTo(Flat::class);
}
}
class Resident
{
public function floor()
{
return $this->belongsTo(Floor::class);
}
}
class Flat
{
public function floors()
{
return $this->hasMany(Floor::class);
}
public function residents()
{
return $this->hasManyThrough(Resident::class, Floor::class);
}
}

Get posts with tags having a column value not more than 7

Say, I have two entities in many-to-many relationship:
posts
-id
-title
-body
tags
-id
-title
-sequence (int)
post_tag
-post_id
-tag_id
I want to grab those Posts which have the tags with the last sequence value of 7. Bear with me. An example will make more sense:
post_tag:
=========
-post_id: 1
-tag_id: 3
-post_id: 1
-tag_id: 4
tags:
=====
-id: 3
-sequence: 2
-id: 4
-sequence: 7
Post ID 1 should be returned
post_tag:
=========
-post_id: 2
-tag_id: 4
-post_id: 2
-tag_id: 5
tags:
=====
-id: 4
-sequence: 7
-id: 5
-sequence: 8
Post ID 2 should NOT be returned because it has a tag whose sequence exceeds 7.
post_tag:
=========
-post_id: 3
-tag_id: 2
-post_id: 3
-tag_id: 3
-post_id: 3
-tag_id: 4
tags:
=====
-id: 2
-sequence: 1
-id: 3
-sequence: 2
-id: 4
-sequence: 7
Post ID 3 should be returned
Here is what I have tried so far:
$posts = Post::whereHas('tags', function($q){ $q->where('sequence', 7);})->get();
But it returns even those posts whose sequence is upper than 7. And I am not complaining. I know why it returns the posts with tags having sequence greater than 7. I just don't know how to solve this.
Hint: In terms of query, the problem can be thought of as:
Not more than X and X inclusive
Probably what you are looking for is whereDoesntHave()
$posts = Post::whereHas('tags', function ($q) {
$q->where('sequence', 7);
})
->whereDoesntHave('tags', function($q) {
$q->where('sequence', '>', 7);
})->get();
What you actually need is a check for equality and less than, like this:
$posts = Post::whereHas('tags', function($q) {
$q->where('sequence', '<=', 7);
})->get();

Stata: foreach creates too many variables -

I created a toy example of my code below.
In this toy example I would like to create a measure of all higher prices minus lower prices within a self-created reference group. So within each reference group, I would like to take each individual and subtract its price value from all higher price values from other individuals in the same group. I do not want to have negative differences. Then I would like to sum all these differences. In creating this code I found some help here:
http://www.stata.com/support/faqs/data-management/try-all-values-with-foreach/
However, the code didn't work perfectly for me, because my dataset is quite large (several 100K obs) and the examples on the website and my code only work until the numlist maximum of 1600 in Stata. (I am using version 12). The toy example with the auto dataset works, due to small size of the dataset.
I would like to ask if someone has an idea how to code this more efficiently, so that I can get around the numlist restriction. I thought about summing the differences directly without saving them in intermediate variables, but that also blow up the numlist restriction.
clear all
sysuse auto
ren headroom refgroup
bysort refgroup : egen pricerank = rank(price)
qui: su pricerank, meanonly
gen test = `r(max)'
su test
foreach i of num 1/`r(max)' {
qui: bys refgroup: gen intermediate`i' = price[_n+`i'] -price if price[_n+`i'] > price
}
egen price_diff = rowmax(intermediate*)
drop intermediate*
If I understand this correctly, this isn't even a problem that requires explicit loops. The sum of all higher prices is just the difference between two cumulative sums. You might need to think through what you want to do if prices are tied.
. clear
. set obs 10
obs was 0, now 10
. gen group = _n > 5
. set seed 2803
. gen price = ceil(1000 * runiform())
. bysort group (price) : gen sumhigherprices = sum(price)
. by group : replace sumhigherprices = sumhigherprices[_N] - sumhigherprices
(10 real changes made)
. list
+--------------------------+
| group price sumhig~s |
|--------------------------|
1. | 0 218 1448 |
2. | 0 264 1184 |
3. | 0 301 883 |
4. | 0 335 548 |
5. | 0 548 0 |
|--------------------------|
6. | 1 125 3027 |
7. | 1 213 2814 |
8. | 1 828 1986 |
9. | 1 988 998 |
10. | 1 998 0 |
+--------------------------+
Edit: For what the OP needs, there is an extra line
. by group : replace sumhigherprices = sumhigherprices - (_N - _n) * price
If I understand the wording of the problem correctly, maybe this can help. It uses joinby (new observations are created and depending on the size of the original database, you may or not hit the Stata hard-limit on number of observations). The code reproduces the results that would follow from the code of the original post. This is a second attempt. The code before this final edit did not provide the sought-after results. The wording of the problem was somewhat difficult for me to understand.
clear all
set more off
* Load data
sysuse auto
* Delete unnecessary vars
ren headroom refgroup
keep refgroup price
* Generate id´s based on rankings (sort)
bysort refgroup (price): gen id = _n
* Pretty list
order refgroup id
sort refgroup id price
list, sepby(refgroup)
* joinby procedure
tempfile main
save "`main'"
rename (price id) =0
joinby refgroup using "`main'"
list, sepby(refgroup)
* Do not compare with itself and drop duplicates
drop if id0 >= id
* Compute differences and max
gen dif = abs(price0 - price)
collapse (max) dif, by(refgroup id0)
list, sepby(refgroup)

Resources