Eloquent Joins and Sums - laravel

I have 2 tables and would like to get have a query which gets all columns from table 'projects' and several sums from table 'invoices' based on a field 'type'.
projects
+----+--------+------+--------+
| ID | Address| Date | Other |
+----+--------+------+--------+
| 1 | demo | date | other |
| 2 | demo2 | date2| other2 |
invoices
+----+---------+-----+--------+
| ID | proj_id | type| amount |
+--------------+-----+--------+
| 1 | 1 | a | 10 |
| 2 | 1 | a | 20 |
| 3 | 1 | b | 10 |
| 4 | 1 | b | 15 |
| 5 | 1 | c | 5 |
| 6 | 2 | a | 30 |
| 7 | 2 | a | 5 |
| 8 | 2 | b | 30 |
| 9 | 2 | c | 5 |
| 10 | 2 | c | 30 |
Using Laravel Eloquent I want to be able to get:
+----+---------+------+-------+---+---+---+
| ID | Address | Date | Other | a | b | c |
+----+---------+------+-------+---+---+---+
| 1 | demo | date | other |30 |25 | 5 |
| 2 | demo2 | date2| other2|35 |30 |35 |
Im getting stuck with the sum part, well actually the whole thing!
So far I have:
$projects = DB::table('projects')
->leftJoin('invoices', 'projects.id', '=', 'invoices.project_id')
->select('projects.*', 'invoices.*')
->get();
Which is obviously not very far along! Any help would be greatly appreciated!

You need a basic pivot query here. The easiest way to go here would probably be via a raw select:
$sql = "projects.*, ";
$sql .= "sum(case when invoices.type = 'a' then invoices.amount end) as a, ";
$sql .= "sum(case when invoices.type = 'b' then invoices.amount end) as b, ";
$sql .= "sum(case when invoices.type = 'c' then invoices.amount end) as c";
$projects = DB::table('projects')
->select(DB::raw($sql))
->leftJoin('invoices', 'projects.id', '=', 'invoices.project_id')
->groupBy('project.id')
->get();
This should correspond to the following raw MySQL query:
SELECT
p.*,
SUM(CASE WHEN i.type = 'a' THEN i.amount END) AS a,
SUM(CASE WHEN i.type = 'b' THEN i.amount END) AS b,
SUM(CASE WHEN i.type = 'c' THEN i.amount END) AS c
FROM project p
LEFT JOIN invoices I
ON p.id = i.project_id
GROUP BY
p.id;

Related

Laravel leftJoin returns null from 2nd table

I have 2 table duty_sheets
centerId | centerName | p1 | p2 | p3 | p4 | ...p22 | examiId
1 | xyz | 1 | 5 | 8 | 7 | 1 | 1
2 | abc | 9 | 1 | 6 | 6 | 1 | 1
and feedback
id | centerId | inspectorId | A | B | C | examiId
1 | 1 | 1 | 1 | 5 | 8 | 1
2 | 2 | 9 | 9 | 1 | 6 | 1
here is my code
$center = DutySheet::select('duty_sheets.centerId', 'duty_sheets.centerName','feedback.id')
->leftJoin('feedback', function ($leftJoin) {
$leftJoin->on('duty_sheets.examId', 'feedback.examId')
->where("duty_sheets.centerId", 'feedback.centerId')
->where("feedback.inspectorId", 1);
})
->where("duty_sheets.examId", 1)
->where("p20", 1)
->get();
dd($center);
to retrieve "All rows from DutySheet where p20 = 1 and dutysheet.examId = 1, and relevant rows from feedback depend on centerId, inspectorId and examId.
The problem is that the query return feedback.id as null while the record exist in feedback table with the ids.
Laravel version = 9
The problem is in left Join
->where("duty_sheets.centerId", 'feedback.centerId')
This build a where against the value 'feedback.centerId'
duty_sheets.centerId='feedback.centerId'
You need use
->on("duty_sheets.centerId",'=', 'feedback.centerId')
Or
->whereColumn("duty_sheets.centerId", 'feedback.centerId')

Laravel Select unique count with groupBy

I am trying to get the count of unique batches in gift_code table for each campaign. The gift_code table is joined to campaign table by campaign_id.
Here is some sample data for campaign table.
--------------+--------------
|campaign_id | name |
--------------+--------------
| 1 | abc |
--------------+--------------
| 2 | xyz |
--------------+--------------
Below is some sample data for gift_code table.
--------------+------------------------+--------------+
|gift_code_id | campaign_id | batch | unique_code |
--------------+-------------+----------+---------------
| 1 | 1 | 1 | zxc23 |
--------------+-------------+----------+--------------+
| 2 | 1 | 2 | rtc26 |
--------------+-------------+----------++-------------+
| 3 | 2 | 1 | z8723 |
--------------+-------------+----------+--------------+
| 4 | 2 | 2 | h7c26 |
--------------+-------------+----------++-------------+
| 5 | 2 | 2 | rrcf6 |
--------------+-------------+----------++-------------+
| 6 | 2 | 3 | r7y28 |
--------------+-------------+----------++-------------+
| 7 | 2 | 3 | bnc26 |
--------------+-------------+----------++-------------+
$campaign = DB::table('campaign')
->select('campaign.*', DB::raw('count(gift_code.batch) as batch_count')->groupBy('gift_code.campaign_id')->groupBy('gift_code.batch'))
->leftjoin('gift_code', 'campaign.campaign_id', '=', 'gift_code.campaign_id')
->get();
My expected results are:
--------------+-------------------------+
|campaign_id | name |batch_count|
--------------+-------------+-----------+
| 1 | abc | 2 |
--------------+-------------+-----------+
| 2 | xyz | 3 |
--------------+-------------+-----------+
Try below query
$data = \DB::table('campaign as c')
->leftJoin('gift_code as gc','c.campaign_id','=','gc.campaign_id')
->select('c.*',\DB::raw('COUNT(distinct(gc.batch)) as batch_count'))
->groupBy('c.campaign_id')
->get();

How to categorize to age ranges and find the top 3?

Having a hive table with age column consisting of age of persons.
Have to count and display the top 3 age categories.
Ex: whether below 10, 10-15, 15-20, 20-25, 25-30, ...
Which age category appears more.
Please suggest me a query to do this.
select case
when age <= 10 then '0-10'
else concat_ws
(
'-'
,cast(floor(age/5)*5 as string)
,cast((floor(age/5)+1)*5 as string)
)
end as age_group
,count(*) as cnt
from mytable
group by 1
order by cnt desc
limit 3
;
You might need to set this parameter:
set hive.groupby.orderby.position.alias=true;
Demo
with mytable as
(
select floor(rand()*100) as age
from (select 1) x lateral view explode(split(space(100),' ')) pe
)
select case
when age <= 10 then '0-10'
else concat_ws('-',cast(floor(age/5)*5 as string),cast((floor(age/5)+1)*5 as string))
end as age_group
,count(*) as cnt
,sort_array(collect_list(age)) as age_list
from mytable
group by 1
order by cnt desc
;
+-----------+-----+------------------------------+
| age_group | cnt | age_list |
+-----------+-----+------------------------------+
| 0-10 | 9 | [0,0,1,3,3,6,8,9,10] |
| 25-30 | 9 | [26,26,28,28,28,28,29,29,29] |
| 55-60 | 8 | [55,55,56,57,57,57,58,58] |
| 35-40 | 7 | [35,35,36,36,37,38,39] |
| 80-85 | 7 | [80,80,81,82,82,82,84] |
| 30-35 | 6 | [31,32,32,32,33,34] |
| 70-75 | 6 | [70,70,71,71,72,73] |
| 65-70 | 6 | [65,67,67,68,68,69] |
| 50-55 | 6 | [51,53,53,53,53,54] |
| 45-50 | 5 | [45,45,48,48,49] |
| 85-90 | 5 | [85,86,87,87,89] |
| 75-80 | 5 | [76,77,78,79,79] |
| 20-25 | 5 | [20,20,21,22,22] |
| 15-20 | 5 | [17,17,17,18,19] |
| 10-15 | 4 | [11,12,12,14] |
| 95-100 | 4 | [95,95,96,99] |
| 40-45 | 3 | [41,44,44] |
| 90-95 | 1 | [93] |
+-----------+-----+------------------------------+

laravel eloquent relation with 3 table

Hi I want query from 3 table
user:
+----+-----------+
| id | name |
+----+-----------+
| 1 | Denny |
| 2 | Agus |
| 3 | Dini |
| 4 | Angel |
+----+-----------+
History_Education
+----+-----------+-------------+
| id | userId | educationId |
+----+-----------+-------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
+----+-----------+-------------+
Education
+----+-----------+----------+
| id | level | Name |
+----+-----------+----------+
| 1 | 1 | SD |
| 2 | 2 | SMP |
| 3 | 3 | SMA |
| 4 | 4 | S1 |
+----+-----------+----------+
How to query with laravel Eloquent to get the latest user education order by Level DESC
expected:
+----+-----------+----------------------+
| id | Name | Latest_Education |
+----+-----------+----------------------+
| 1 | Denny | SMP |
| 2 | Agus | SMP |
| 3 | Dini | - |
| 4 | Angel | - |
+----+-----------+----------------------+
In normal query:
select id,name ,(select E.name from education E inner join History_eductaion HE on E.id = HE.education_id where HE.userId =U.id limit 1 order by E.level DESC )latest_education from USER U
How to translate to laravel eloquent?
The quick answer is use the query builder - your query needed to be altered slightly to match the table names and columns as listed in your question:
$result = DB::table('user')
->select([
'id',
'name',
DB::raw("(select E.name from Education E inner join History_Education HE on E.id = HE.educationId where HE.userId = user.id order by E.level DESC limit 1) as latest_education")
])
->get();

Oracle left join

I have two tables A1,A2
A1 (primary key ID):
| ID | NAME |
|-------|---------|
| 1 | Cat1 |
| 2 | Cat2 |
| 3 | Cat3 |
| 4 | Cat4 |
| 5 | Cat5 |
and A2 (primary key ID, foreign key A1_ID=A1.ID)
| ID | NAME | A1_ID | TYPE |
|-------|---------|--------|--------|
| 1 | Sub1 | 1 | L |
| 2 | Sub2 | 2 | F |
| 3 | Sub3 | 3 | V |
| 4 | Sub4 | 4 | L |
| 5 | Sub5 | 4 | V |
| 6 | Sub6 | 5 | |
I am trying to get all the results from both tables where A2.Type is L or F or null
This is what I have up to now:
select a.*, b.*
from a1 a
left join a2 b
on a.id=b.a1_id
where (b.type='L'
or b.type='F'
or b.type is null)
which returns :
| ID | NAME | ID | NAME | A1_ID | TYPE |
|-------|---------|--------|--------|--------|--------|
| 1 | Cat1 | 1 | Sub1 | 1 | L |
| 2 | Cat2 | 2 | Sub2 | 2 | F |
| 4 | Cat4 | 4 | Sub4 | 4 | L |
| 5 | Cat5 | 6 | Sub6 | 5 | |
But I am looking for a query that it will exclude the line with A1.ID = 4 because with the same A1_ID there is a row with TYPE=V
| ID | NAME | ID | NAME | A1_ID | TYPE |
|-------|---------|--------|--------|--------|--------|
| 1 | Cat1 | 1 | Sub1 | 1 | L |
| 2 | Cat2 | 2 | Sub2 | 2 | F |
| 5 | Cat5 | 6 | Sub6 | 5 | |
Any ideas?
You can do this with not exists:
select a.*, b.*
from a1 a left join
a2 b
on a.id = b.a1_id
where (b.type = 'L' or b.type='F' or b.type is null) and
not exists (select 1 from a2 where a2.id = a.id and a2.type = 'V');
Your original query doesn't quite do what your text says. This seems to be what you are describing:
select a.*, b.*
from a1 a join
a2 b
on a.id = b.a1_id and
(b.type = 'L' or b.type='F' or b.type is null)
where not exists (select 1 from a2 where a2.id = a.id and a2.type = 'V');
That is, the conditions in the where clause are moved to the on clause and the join is changed to an inner join. The difference is when there are no matches in a2 for a given id. Your version would return the row. This version will filter it out.
select a.*, b.*
from a1 a
left join a2 b
on a.id=b.a1_id
left join a2 c
on c.a1_ID = b.a1_ID AND c.type = 'V'
where (b.type='L'
or b.type='F'
or b.type is null)
and c.type is null
This is one way. If all you ever need to consider is v this should be efficient. However, if you need to adjust based on other criteria there maybe a better way.
in essence this takes your current results and compares it to another set of a2 that only contains record type "V". If any match is found, it is excluded from the results.

Resources