I performed a raw SQL query in Doctrine 1.4 (in order to acheve advanced subqueries) and obtained a joined resultset with data from different tables (Evaluation and User):
(this is what I have in the array $all:)
id user_id guide_id group_id level date score max_score last_improvement total_improvement created_at updated_at email password salt first_name last_name is_active is_super_admin last_login verified_email entity_type entity_name country_id state_id source raw preferences evaluation_count
52 52 2 2001 1 2014-06-19 245 245 2014-05-19 16:24:31 2014-08-08 17:13:20 martin#mindset3.com 040000000000000secret0000b 4da00nothingtoseehere067c Martín (municipio) 1 1 1 municipio Martincipio 0 0 12 17
52 52 2 2002 1 2014-06-26 122 170 -48 -48 2014-05-19 16:24:31 2014-08-08 17:13:20 martin#mindset3.com 0000000000000000000000009b 4d00000movealong000c0c67c Martín (municipio) 1 1 1 municipio Martincipio 0 0 12 17
So I did:
$ret = new Doctrine_Collection('Evaluation');
$ret->fromArray($all);
which successfully populated a collection of "Evaluations", but I also need the "User" associated with each evaluation. The information I need is already in the resultset, but the collection does not automatically create the User object for each Evaluation object.
Then in the view I refer to the relation to print the name of the user, and Doctrine performs a large amount of queries that are not really necesary.
I know there are several ways of avoiding that, but the more natural way (I think) is through the population of the related objects and then accessing them the regular way.
How can that be done?
I figured it out.
I had to iterate the resultset creating a "User" object myself before feeding it into Evaluation::fromArray():
Here's how:
$q->execute($params);
$all = $q->fetchAll(PDO::FETCH_ASSOC);
Utilities::array2table($all); // here I print the raw PDO resultset to debug it
foreach($all as $k => $v){
$u = new User();
$u->fromArray($v);
$all[$k]['User'] = $u;
// Create a Doctrine "User" and append it to the resultset
}
// Now create the evaluations just like I was before:
$ret = new Doctrine_Collection('Evaluation');
$ret->fromArray($all);
return $ret;
Related
Let's say we have two tables:
Payments
id
reason_id
amount
1
1
100
2
2
10
3
1
30
Payment Reasons
id
title
is_discount
1
Payment for something
0
2
Discount for something
1
I'd like to query payments table and sum amount column based on its relationship payment_reasons.is_discount value:
total_received
total_discounted
130
10
How to achieve this?
If you want to get the data in one db-request, I don't think there is a clean laravel-way to solve this. I've been trying out some raw mysql to get it into a working query and it would look like this:
select
sum(if(pr.is_discount = 0, p.amount, 0)) as total_received,
sum(if(pr.is_discount = 1, p.amount, 0)) as total_discounted
from payments p
join payment_reasons pr on pr.id = p.reason_id
The if statement will render if discount needs to be used per row and the sum wil simply sum all the end-results together.
Doing this in laravel with a model does not really make sence, since there are no fields in the result which your model could use anyway. In this case, using eloquent would be more (unmaintainable) code than a simple Query Builder:
$data = DB::select('
select
sum(if(pr.is_discount = 0, p.amount, 0)) as total_received,
sum(if(pr.is_discount = 1, p.amount, 0)) as total_discounted
from payments p
join payment_reasons pr on pr.id = p.reason_id
')
If you don't mind doing multiple queries to fetch the data, models make more sense:
$totalReceived = Payment
::whereHas('paymentReason', fn($q) => $q->where('is_discount', 0))
->sum('amount');
$totalDiscounted = Payment
::whereHas('paymentReason', fn($q) => $q->where('is_discount', 1))
->sum('amount');
Please note that this example will perform 4 queries, 2 on payments, and 2 on payment_reasons
Let's say you have these two tables:
CARS
ID CAR_MODEL
11 Mustang
22 Camaro
33 F-150
PARTS
ID CAR_ID PART_NAME
1 11 Steering Wheel
2 22 Steering Wheel
3 22 Headlights
You need to create a search form. How would you you make this work:
var query = db.CARS.include(u => u.PARTS);
if(model.CarPartName != "")
query = query.where(u => u.PARTS.PART_NAME == model.CarPartName); //this doesn't work
return query.ToList();
There is currently a foreign key relationship between PARTS.CAR_ID and CARS.ID but for some reason when I attempt u.PARTS. I can't append on a column name.
Any idea how to make this optional join work?
That wouldn't work that way, because a car can have many parts, thus u.PARTS is returning a collection. There are many solutions to this problem, you can start by Cars collection (as you did), or you can start by PARTS collection. If you started with PARTS collection, it would look like:
var query = db.PARTS.Include(p => p.Car);
if(model.CarPartName != "")
query = query.Where(u => u.PART_NAME == model.CarPartName);
return query.Select(p => p.Car).Distinct().ToList();
With Cars:
var query = db.CARS.include(u => u.PARTS);
if(model.CarPartName != "")
query = query.Where(u => u.PARTS.Any( up => up.PART_NAME == model.CarPartName));
return query.ToList();
Note: I added the first one, just because I wanted to show what is going on.
I have an array with User IDs:
UserID = [
1
2
3
4
5
3
2
2
3]
I have a table that maps User ID to User Email. I'm trying to match IDs to Emails but retain duplicates so I end up with:
[
1#me.com
2#me.com
3#me.com
4#me.com
5#me.com
3#me.com
2#me.com
2#me.com
3#me.com]
When I try to pluck or map it like:
UserInfo.where(:id => UserID).pluck(:email)
I end up with:
[
1#me.com
2#me.com
3#me.com
4#me.com
5#me.com]
How can I get the table I'm looking for?
You will have to loop
ids.map{|id| UserInfo.find(id: id).email }
But of you have tons of ids you may want to optimize it by loading first just the records you need
email_hash = UserInfo.where(id: ids).inject({}) {|h, v| h[v.id] = v.email; h;}
result = ids.map{|id| email_hash[id]}
I'm using yajra's laravel/datatables plugin and I want to send only the first row of every group from the query sorted according to date of the record descending. This is my query :
$qsrecords = QualityScore::where('clientID', '=', $user['id'])
->whereBetween('day', array($startDate, $endDate))
->where($desiredValue, $operator, $quantity)
->where('previousQualityScore','!=','0');
This query returns every record for this given user id like :
Client ID | Keyword ID | Quality Score | Date
2 81 8 21.08.2016
2 42 9 19.08.2016
2 81 7 16.08.2016
2 42 5 14.08.2016
as you can see, ı got 2 different keywords and my query is giving that output.
but i want my query to generate results like :
Client ID | Keyword ID | Quality Score | Date
2 81 8 21.08.2016
2 42 9 19.08.2016
Only the last records of every keyword. That's I want to achieve.
The way i send the query to view :
// Send data to view via datatables plugin
return Datatables::of($qsrecords)->make(true);
Try this
QualityScore::select( * , DB::raw('MAX(date) as date'))
->where('clientID', '=', $user['id'])
->whereBetween('day', array($startDate, $endDate))
->where($desiredValue, $operator, $quantity)
->where('previousQualityScore','!=','0');
->groupBy('keyword_id')
->get();
Finally i've figured it out! I've changed the way i approach to this issue but the following code has solved the issue, thanks #Kiran-sadvilkar for suggestions.
$groupByMaxDateQuery = ' SELECT qs.adGroup,qs.keyword, qs.previousQualityScore, qs.qualityScore, qs.qualityScoreDifference, qs.day
FROM homestead.qualityScore AS qs
INNER JOIN (
SELECT adGroupID, keywordID, max(day)
AS MaxDay
FROM homestead.qualityScore
GROUP BY adGroupID,keywordID
)
innerTable ON qs.adGroupID = innerTable.adGroupID
AND qs.keywordID = innerTable.keywordID
AND qs.day = innerTable.MaxDay
WHERE qs.clientID = '.$user['id'].' AND
qs.day BETWEEN "'.$startDate.'" AND "'.$endDate.'" AND
qs.'.$desiredValue.' '.$operator.' '.$quantity.' AND
qs.previousQualityScore != 0
';
// Pull data from database with current conditions
$qsrecords = DB::table(DB::raw("($groupByMaxDateQuery) as qs"));
As a result i've decided to use a raw sql query and now it works...
I have a T-SQL 2005 query which returns:
pid propertyid displayname value
----------- ----------- --------------- ---------------
14270790 74 Low Price 1.3614
14270790 75 High Price 0
14270791 74 Low Price 1.3525
14270791 75 High Price 0
14270792 74 Low Price 1.353
14270792 75 High Price 0
14270793 74 Low Price 1.3625
14270793 75 High Price 0
14270794 74 Low Price 1.3524
14270794 75 High Price 0
What I would like to do is essentially pivot on the displayname field, hopefully producing:
pid Low Price High Price
14270790 1.3614 0
14270791 1.3525 0
14270792 1.353 0
14270793 1.3625 0
14270794 1.3524 0
(Not sure how the propertyid field would be output, so I left it out (was hoping it would simply sit alongside the Low Price and High Price fields, to indicate their IDs, but I don't think that will work.)
The problem is that the content of the original displayname field is dynamic - it is produced from a join with a PropertyName' table, so the number of pivoted columns is variable. It could therefore containHigh Price,Low Price,OpenandClose`, depending on what the join with that table returns.
It is, of course, relatively easy (regardless of the trouble I'm having writing the initial query!) to produce this pivot in a fixed query or stored proc. However, is it possible to get LINQ to generate a SQL query which would name each column to be produced rather than having to write a dynamic (probably in a stored proc) query which lists out the column names?
Thanks,
Matt.
I'll give you a sample with a different data (that I needed). You can adapt that to your need. Note only two linq queries are used, most of the other fluff is to convert a list into a datatable.
var data = new[] {
new{Student=1, Subject="English", Marks=40},
new{Student=1, Subject="Maths", Marks=50},
new{Student=1, Subject="Science", Marks=60},
new{Student=1, Subject="Physics", Marks=70},
new{Student=1, Subject="Chemistry", Marks=80},
new{Student=1, Subject="Biology", Marks=90},
new{Student=2, Subject="English", Marks=4},
new{Student=2, Subject="Maths", Marks=5},
new{Student=2, Subject="Science", Marks=6},
new{Student=2, Subject="Physics", Marks=7},
new{Student=2, Subject="Chemistry", Marks=8},
new{Student=2, Subject="Biology", Marks=9}
};
/*Here the pivot column is the subject and the static column is student
group the data against the static column(s)*/
var groups = from d in data
group d by d.Student into grp
select new
{
StudentId = grp.Key,
Marks = grp.Select(d2 => new { d2.Subject, d2.Marks }).ToArray()
};
/*get all possible subjects into a separate group*/
var subjects = (from d in data
select d.Subject).Distinct();
DataTable dt = new DataTable();
/*for static cols*/
dt.Columns.Add("STUDENT_ID");
/*for dynamic cols*/
foreach (var subject in subjects)
{
dt.Columns.Add(subject.ToString());
}
/*pivot the data into a new datatable*/
foreach (var g in groups)
{
DataRow dr = dt.NewRow();
dr["STUDENT_ID"] = g.StudentId;
foreach (var mark in g.Marks)
{
dr[mark.Subject] = mark.Marks;
}
dt.Rows.Add(dr);
}
This is the closest I could get, but it's not LINQ...
create table #t
(
pointid [int],
doublevalue [float],
title [nvarchar](50)
)
insert into #t
select
distinct top 100
v.pointid, v.doublevalue, p.title
from [property] p
inner join pointvalue v on p.propertyid = v.propertyid
inner join point pt on v.pointid = pt.pointid
where v.pointid in (select top 5 p.pointid from point p where p.instanceid = 36132)
declare #fields nvarchar(250)
set #fields = (select STUFF((SELECT N',[' + title + ']' FROM [property] FOR XML PATH('')), 1, 1, N''))
--select #fields
declare #sql nvarchar(500)
set #sql = 'select * from #t
pivot
(
sum(doublevalue)
for [title] in ('+#fields+')
) as alias'
--select #sql
exec (#sql)
drop table #t
The kicker is that I'm simply asking for every entry in the Property table, meaning there's a lot of columns, in the resulting pivot, which have NULL values.
the code I think is like this:
var list = from table in Property
group table by table.pid into g
select new
{
pid = g.key,
LowPrice = g.Where(w => w.pid== g.key && w.priceType == "low").Select(s => s.value).FirstorDefault(),
HighPrice = g.Where(w => w.pid== g.key && w.priceType == "high").Select(s => s.value).FirstorDefault(),
};
Hope it can help you and have a nice day.