how to make a subquery using the query builder of laravel - sql

I want to do the following mysql query with the query builder of Laravel 7:
SELECT r.id, (SELECT date FROM posts as pp where r.id = pp.request_id
ORDER BY STR_TO_DATE(SUBSTRING(pp.date, 1, 19),'%Y-%m-%dT%H:%i:%s') DESC limit 1), r.delivery_mode FROM requests AS r
LEFT JOIN posts AS p ON r.id = p.request_id
group by r.id;
However unfortunately I don't understand how to obtain the latests post date for every request using the query builder of Laravel. I suppose I should do a subquery as in the query above. This is my code:
$query = DB::table('requests');
$query->select(DB::raw('DISTINCT(requests.id)'),'requests.delivery_mode',DB::raw('posts.date'));
$query->leftJoin('posts', 'requests.id', '=', 'posts.request_id');
$requests_list = $query->get();
In the table posts, the date is a string in this format: 2020-04-16T12:46:33+02:00. So I used that complicated functions because I want to see only the latest date post grouped by id_request, that is the foreign key of the table posts connected with the primary key id of the table requests. A request can have many posts.
Can help?
PS:
I found the solution:
$query = DB::table('requests');
$query->select(DB::raw('DISTINCT(requests.id)'),'requests.delivery_mode',DB::raw("(SELECT date FROM posts as pp where requests.id = pp.request_id ORDER BY STR_TO_DATE(SUBSTRING(pp.date, 1, 19),'%Y-%m-%dT%H:%i:%s') DESC limit 1) as lastPostDate"));
$query->leftJoin('posts', 'requests.id', '=', 'posts.request_id');
$requests_list = $query->get();
Probably it isn't a pretty solution but it works!.

Request Model
public function posts()
{
return $this->hasMany(Post::class);
}
Post Model
public function request()
{
return $this->belongsTo(Request::class);
}
Controller
public function index()
{
$date = Post::has("request")
->orderBy(...) // add your order by condition here
->limit(1)
->pluck("date");
$requests = Request::with("posts")
->where("date", $date)
->get();
return $requests;
}

Related

Transforming Raw Sql to Laravel equolent

I have written this SQL code
SELECT drugs.*, COUNT(*) as 'views' from drugs INNER JOIN drug_seen on drugs.id = drug_seen.drug_id GROUP BY drugs.id order by views ASC
And now I am trying to write in in the Laravel equolent but I am facing some troubles.
This is what I have tried
$drugs = Drug::select(DB::raw('drugs.*,count(*) as views'))
->join('drug_seen', 'drugs.id', 'drug_seen.drug.id')
->groupBy('drug.id')->orderByRaw('views');
I am having errors like column not found i think the code is not written properly
Drug class
class Drug extends Model
{
use HasFactory;
use SoftDeletes;
...
...
...
public function drugVisits()
{
return $this->hasMany(DrugSeen::class);
}
Hop this will solve your problem.
$drugs = Drug::with('drugVisits')->get();
$drugs->count(); //for total records in drugs table.
You have typo error in join instead on drug_id you use drug.id
Try this:
$drugs = Drug::select(DB::raw('drugs.*,count(*) as views'))
->join('drug_seen', 'drugs.id', 'drug_seen.drug_id')
->groupBy('drugs.id')->orderByRaw('views');
}
As soon as you use join() you're leaving Eloquent and entering Query\Builder, losing the benefits of Model configurations in the process. And with() eager-loads aren't the answer, if you're looking to filter the results by both tables. What you want is whereHas().
Also, as far as your grouping and count manipulation there, I think you're looking more for Collection handling than SQL groups.
$drugModel = app(Drugs::class);
$results = $drugModel->whereHas('drugVisits')->with('drugVisits')->get();
$organizedResults = $results
->groupBy($drugModel->getKey())
->sortyBy(function (Drugs $drugRecord) {
return $drugRecord->drugVisits->count();
});
If you want to have a 'views' property that carries the count in the root-level element, it would look like this:
$drugModel = app(Drugs::class);
$results = $drugModel->whereHas('drugVisits')->with('drugVisits')->get();
$organizedResults = $results
->groupBy($drugModel->getKey())
->map(function (Drugs $drugRecord) {
$drugRecord->views = $drugRecord->drugVisits->count();
return $drugRecord;
});
->sortyBy('views');

Selecting a SubQuery from a DetachedCriteria

I have the following criteria and detachedCriteria.
var criteria = Session.CreateCriteria<ItemAnalysis>("ia");
criteria.CreateAlias("ia.ItemInstance", "ii");
criteria.CreateAlias("ii.ScoreAdministration", "sa");
criteria.Add(Restrictions.Eq("ii.ItemId", itemId));
var status = DetachedCriteria.For<ItemAnalysis>("ia_")
.CreateAlias("ia_.ItemInstance", "ii_")
.CreateAlias("ii_.ScoreAdministration", "sa_")
.Add(Restrictions.Eq("ii_.ItemId", itemId))
.SetProjection(
Projections.SqlProjection
(
"ia_.CTTItemStatId, RANK() OVER(Partition BY
sa_.ExamSeriesCode ORDER BY ia_.StatDate DESC) AS RowNm",
new string[] { "CTTItemStatId", "RowNm" },
new IType[] { NHibernate.NHibernateUtil.Int32,
NHibernate.NHibernateUtil.Int32 }
)
);
I need a way to get a projection or second subquery from the detached query that has just that property and adds a restriction of RowNm=1. I looked at DetachedCriteria.CreateCriteria but that requires an Association Path. I haven't found any similar examples. I did try
// what parameters should I be using here??
var subQuery = status.CreateCriteria(?, ?) // expects an association path and an alias
.Add(Restrictions.Eq("RowNm", 1))
.SetProjection(
Projections.Property("CTTItemStatId")
);
criteria = criteria.Add(Subqueries.PropertyEq("CTTItemStatId", subQuery));

How to get other column value in different table into the query?

I had searching application, finding personal information which had been filtered by some criteria (category, years of experience etc)
I had problem with the last filter, 'tempoh perkhidmatan by negeri'. I need to calculate the number of working experience by state(negeri). For example, when searching for people of 5 years in the state(negeri) 'x', the sql will sum years of experience of each person in the state selected.
This is the full code of SQL searching by criteria:
$query = DB::table('itemregistrations')
->join('sections', 'itemregistrations.SectionID', '=', 'sections.SectionID')
->join('categories', 'itemregistrations.CategoryID', '=', 'categories.CategoryID')
->join('operasi', 'itemregistrations.OperasiID', '=', 'operasi.OperasiID')
->join('negeri', 'itemregistrations.NegeriID', '=', 'negeri.NegeriID')
->join('gred', 'itemregistrations.GredID', '=', 'gred.GredID')
->where('itemregistrations.statusProID', '=', 1)
->select('itemregistrations.name','sections.sectionname', 'categories.categoryname', 'operasi.operasiname', 'itemregistrations.Nobadan', 'itemregistrations.lahir_yy', 'itemregistrations.pdrm_yy', 'gred.namagred', 'itemregistrations.itemRegistrationID', '');
if($request->input('negeri_lahir') != ''){
$query->where('itemregistrations.NegeriID', $request->input('negeri_lahir'));
}
if($request->input('kategori') != '') {
$query->where('itemregistrations.CategoryID', $request->input('kategori'));
}
if($request->input('pangkat') != '') {
$query->where('itemregistrations.OperasiID', $request->input('pangkat'));
}
if(request('umur')) {
$query->whereRaw('YEAR(CURDATE()) - lahir_yy >= ?', [request('umur')]);
}
if($request->input('gred') != '') {
$query->where('itemregistrations.GredID', $request->input('gred'));
}
if(request('tempoh')) {
$query->whereRaw('YEAR(CURDATE()) - pdrm_yy >= ?', [request('tempoh')]);
}
if($request->input('negeri_perkhidmatan') != '') {
$query->join('itemregistrationpangkat', 'itemregistrationpangkat.itemRegistrationID', '=', 'itemregistrations.itemRegistrationID')
->where('itemregistrationpangkat.NegeriID', $request->input('negeri_perkhidmatan'));
}
if(request('tempoh_negeri')) {
$query->select(DB::raw('m.itemRegistrationID, sum(m.duration)'))
->from(DB::raw('(SELECT itemRegistrationID, NegeriID, yeartamatkhidmat - yearmulakhidmat as duration FROM itemregistrationpangkat) AS m
RIGHT JOIN itemregistrations ON itemregistrations.itemRegistrationID=m.itemRegistrationID'))
->distinct()
->groupBy('m.itemRegistrationID');
}
$newitem = $query->get();
return response::json($newitem);
The code involve to be solve is this(the last filter):
if(request('tempoh_negeri')) {
$query->select(DB::raw('m.itemRegistrationID, m.NegeriID, sum(distinct m.duration)'))
->from(DB::raw('(SELECT itemRegistrationID, NegeriID, yeartamatkhidmat - yearmulakhidmat as duration FROM itemregistrationpangkat) AS m
RIGHT JOIN itemregistrations ON itemregistrations.itemRegistrationID=m.itemRegistrationID'))
->groupBy('m.itemRegistrationID', 'm.NegeriID');
}
The problem is I need to get name column, sectionID column, CategoryID, OperasiID, NegeriID, GredID, from itemregistrations table from the $query statement. How to combine the last query filter in 'tempoh_negeri' with the previous one?
I didn't know about Laravel in particular, so I had much trouble trying to understand how your query was built, but this syntax seems to enable people to write a request by adding chunks, but not necessarily in the right order. So here's what I believe your query is supposed to do, for SQL speakers:
SELECT itemregistrations .name,
sections .sectionname,
categories .categoryname,
operasi .operasiname,
itemregistrations .Nobadan,
itemregistrations .lahir_yy,
itemregistrations .pdrm_yy,
gred .namagred,
itemregistrations .itemRegistrationID
-- if($tempoh_negeri) (request)
,m .itemRegistrationID,
sum(m.duration)
FROM itemregistrations
-- if($tempoh_negeri) (request)
,(SELECT DISTINCT itemRegistrationID,
NegeriID,
yeartamatkhidmat - yearmulakhidmat as duration
FROM itemregistrationpangkat) AS m
RIGHT JOIN itemregistrations
ON itemregistrations.itemRegistrationID = m.itemRegistrationID
JOIN sections
ON itemregistrations.SectionID = sections.SectionID
JOIN categories
ON itemregistrations.CategoryID = categories.CategoryID
JOIN operasi
ON itemregistrations.OperasiID = operasi.OperasiID
JOIN negeri
ON itemregistrations.NegeriID = negeri.NegeriID
JOIN gred
ON itemregistrations.GredID = gred.GredID
-- if($negeri_perkhidmatan)
JOIN itemregistrationpangkat
ON itemregistrationpangkat.itemRegistrationID = itemregistrations.itemRegistrationID
WHERE itemregistrations.statusProID = 1
-- if($negeri_lahir) (WHERE)
AND itemregistrations.NegeriID = $negeri_lahir
-- if($kategori) (WHERE)
AND itemregistrations.CategoryID = $kategori
-- if($pangkat) (WHERE)
AND itemregistrations.OperasiID = $pangkat
-- if(umur) (WHERERAW) (request)
AND YEAR(CURDATE()) - lahir_yy >= umur
-- if($gred) (WHERE)
AND itemregistrations.GredID = $gred
-- if($tempoh) (WHERERAW) (request)
AND YEAR(CURDATE()) - pdrm_yy >= tempoh
-- if($negeri_perkhidmatan)
AND itemregistrationpangkat.NegeriID = $negeri_perkhidmatan
-- if($tempoh_negeri) (request)
GROUP BY m.itemRegistrationID
If it's so, you cannot do what you want following that way (including main columns into the subquery) because your subquery will be evaluated BEFORE the main one is.
Instead, you need to write a proper filter at main query level, that is : among the others "JOIN…ON" clauses already in place. Which would give:
LEFT JOIN itemregistrationpangkat
ON itemregistrationpangkat.itemRegistrationID = itemregistrations.itemRegistrationID
… then specify the substraction directly in your sum() function
sum(yeartamatkhidmat - yearmulakhidma)
As regards Lavarel, this probably would give something like:
if(request('tempoh_negeri')) {
$query->leftjoin('itemregistrationpangkat','itemRegistrationID','=','itemregistrations.itemRegistrationID');
$query->select(DB:raw('sum(yeartamatkhidmat - yearmulakhidmat'));
}

Forming SQL Query and Passing The Result To The View

I have this view method -
public ActionResult Index()
{
var db = new ApplicationDbContext();
var auctions = db.Auctions.ToArray();
return View(auctions);
}
which correctly returns an array of all auctions in my database. But I want to return just the most popular ones. I want to do something like this:
public ActionResult Index()
{
var db = new ApplicationDbContext();
var auctions = db.Auctions.getMostPopular.ToArray();
return View(auctions);
}
Where getMostPopular() is a method in my model containing all my auctions and looks like so at the moment:
public static List<Auction> getMostPopular()
{
var query = "SELECT* FROM AUCTIONS WHERE EndTime > CONVERT(date, GETDATE()) ORDER BY viewCount DESC;" }
}
So how do I correctly write this getMostPopular() method?
And is this the correct path to go writing it in the model? Or should I write the query in the controller Index action, and if so what would that look like?
Correct is relative. Your proposed SQL statement would seem to do the trick. Ordering the results by the ViewCount desc would appear to be the main part of implementing your method get most popular. If you're already considering a date filter, you might also consider the top clause such as select top 10 * from Table order by ViewCount desc, to only consider a fixed number of most popular items.

Yii left join query

I want to execute the below sql query using Yii framework and need help on this.
SQL query
SELECT t.*, LP.name AS lp_name FROM `user` AS `t` LEFT JOIN `level_profiles` AS `LP` ON t.prof_i = LP.id WHERE t.bld_i IN (17)
So, i tried the below steps.
$usql = 't.bld_i IN (17)';
$criteria1 = new CDbCriteria;
$criteria1->select = 't.*, LP.*';
$criteria1->join = ' LEFT JOIN `level_profiles` AS `LP` ON t.prof_i = LP.id';
$criteria1->addCondition($usql);
$criteria1->order = 't.prof_i';
$result = User::model()->findAll($criteria1);
The above step is not allowing me to access the value from 'level_profiles' table.
Then, i tried to execute:
$usql = 't.bld_i IN (17)';
$result = User::model()->with('level_profiles', array(
'level_profiles'=>array(
'select'=>'name',
'joinType'=>'LEFT JOIN',
'condition'=>'level_profiles.id="prof_i"',
),
))->findAll($usql);
This is returning an error 'Relation "level_profiles" is not defined in active record class "User". '
I know this could be executed using the below method.
Yii::app()->db->createCommand('SELECT query')->queryAll();
But i dont want to use the above.
I am a beginner with Yii and tried to look into the forums. But, i am getting confused how to execute the query using "User::model()" approach .
class User extends CActiveRecord
{
......
public function relations()
{
return array(
'level_porfile_relation'=>array(self::BELONGS_TO, 'Level_Profiles_Modelname', 'prof_i'),
);
}
and your query will be:
$result = User::model()->with('level_porfile_relation')->findAll($usql);