Codeigniter join, group by and count a row from one table - mysql

I'm struggling a bit with this code to get the count result from book_listing (totally) which is the total count of bookings. From the result, some are correct but some are multiplied by 3 or so.
Here is my code:
$this->db->where('list_status', 1)
->where('list_date >=', date('Y-m-d'))
->order_by("user_listings.list_created", "desc")
->limit(18, null)
->select('*, COUNT(user_bookings.book_listing) as totally')
->from('user_listings')
->join('user_images', 'user_images.img_listing = user_listings.list_reference')
->join('user_states', 'user_states.state_id = user_listings.list_state')
->join('user_bookings', 'user_bookings.book_listing = user_listings.list_reference', 'left')
->group_by('user_listings.list_reference')
->get();
Thanks to any help :)

So I solved it, the user_images table seems to be causing the issue due to multiple images being fetched while I need it to return only one.
$this->db->where('list_status', 1)
->where('list_date >=', date('Y-m-d'))
->order_by("user_listings.list_created", "desc")
->limit(18, null)
->select('user_listings.*,image.*,user_states.*')
->select('COUNT(user_bookings.book_listing) as totalBooked')
->from('user_listings')
->join('user_bookings', 'user_bookings.book_listing = user_listings.list_reference', 'left')
->join('(SELECT * FROM user_images WHERE user_images.img_key IN(SELECT MIN(user_images.img_key) FROM user_images GROUP BY user_images.img_listing)) AS image',
'image.img_listing = user_listings.list_reference')
->join('user_states', 'user_states.state_id = user_listings.list_state')
->group_by('user_listings.list_reference')
->get();
Basically, I had to do a select from the join table for user_images. Hope it helps someone else :)

Related

Substraction in MYSQL with some condition

I want to do SUM and SUBTRACTION upon certain condition within mysql query
i have try like this,
asuming the $balance= 655.000.000 then total amount inside my_table = IDR 80.000.000 and USD 500.000
$currency_id = 1;
$balance = 655.000.000;
DB::table('my_table as a')
->join('master_currencies as b', 'a.master_currency_id', 'b.id')
->where('a.id', $id)
->selectRaw("SUM(
CASE
WHEN $currency_id = a.master_currency_id THEN $balance - a.amount
ELSE
a.amount
END) AS net_value, b.symbol, b.id as master_currency_id"
)
->groupBy('b.id')
->get();
The code above should give the result
[
{"net_value":"575.000.000","symbol":"IDR","master_currency_id":1},
{"net_value":"500.000","symbol":"USD","master_currency_id":2}
]
but the result i got is
[
{"net_value":"1.885.000.000","symbol":"IDR","master_currency_id":1},
{"net_value":"500.000","symbol":"USD","master_currency_id":2}
]
Note:
Please ignore dots between numbers, in my database i'm not using these dots. it is for inquiry purposes only
If you have more than one row of IDR then it might be a problem with order of operations.
For example say you have to values (100.000.000) and (50.000.000) then for IDR SUM(655.000.000-100.000.000, 655.000.000-50.000.000)=1.160.000.000 and is not the same as 655.000.000-SUM(100.000.000,50.000.000)=505.000.000 the problem being that each row in the group that you aggregate gets a clean $balance.
You could potentially solve this by doing:
$currency_id = 1;
$balance = 655.000.000;
DB::table('my_table as a')
->join('master_currencies as b', 'a.master_currency_id', 'b.id')
->where('a.id', $id)
->selectRaw("
(CASE
WHEN a.id=$currency_id $balance-SUM(a.amount)
ELSE
SUM(a.amount)
END) AS net_value, b.symbol, b.id as master_currency_id")
->groupBy('b.id')
->get();

where('videos.created_at', '>', '2020.07.26 14:16:29') not working for left joined table - Eloquent, Laravel

everybody.
I have written this code which should return most liked videos posted after the specified date. But the code returns older results than the specified date. I have posted only one video after 2020.07.26 and in spite of this Eloquent returns 8 older videos.
$query = Video::select('videos.id',DB::raw('group_concat(DISTINCT videos.code) AS code'),DB::raw('group_concat(DISTINCT videos.title) AS title'),DB::raw('group_concat(DISTINCT videos.created_at) AS createdDate'),DB::raw('count(reactions.id) AS reactCount'))
->leftJoin('reactions','reactions.at_video','=','videos.id')
->where('reactions.reaction',1)
->orWhereNull('reactions.reaction')
->where('videos.created_at', '>', '2020.07.26 14:16:29')
->groupBy('videos.id')
->orderBy('reactCount','DESC')
->get();
dd($query);
Then I found this code on the stockflow where the solution was whereColumn. But it did not help me. Laravel showed me another error.
whereColumn example from another post on stackoverflow
$query = DB::table('messages')
->leftJoin('participants', function ($join) {
$join->on('messages.thread_id', '=', 'participants.thread_id')
->on('messages.user_id', '!=', 'participants.user_id');
})
->select('messages.created_at as message_date', 'participants.last_read as last_read')
->whereColumn('messages.created_at', '>', 'participants.last_read')->get();
The reason for this is that I group the results. The videos.created field returns the value as many times as the likes of the video. To solve this I used DISTINCT
DB::raw('group_concat(DISTINCT videos.created_at) AS createdDate')
Then I added createdDate in the WHERE() and unfortunately I got one another error again.
->where('createdDate', '>', '2020.07.26 14:16:29')
And this is the error I mean. As many time I got it... I almost loved it ;)
And...
I wrote a query code which returns right results and ran in a native php page.
This is what Laravel should do but it does not...
SELECT videos.id AS id, group_concat(videos.code) AS code, group_concat(videos.title) AS title, group_concat(DISTINCT videos.created_at) AS createdDate, count(reactions.id) AS reactCount
FROM videos
LEFT JOIN reactions ON videos.id = reactions.at_video
WHERE (reactions.reaction = 1 or reactions.reaction IS NULL)
AND videos.created_at > '2020.07.27'
GROUP BY videos.id
ORDER BY reactCount DESC
I thought I would use custom query code in Laravel as the final solution but I have to say but one more again.
$videos = DB::select("SELECT videos.id AS id, group_concat(videos.code) AS code, group_concat(videos.title) AS title, group_concat(DISTINCT videos.created_at) AS createdDate, count(reactions.id) AS reactCount
FROM videos
LEFT JOIN reactions ON videos.id = reactions.at_video
WHERE (reactions.reaction = 1 or reactions.reaction IS NULL)
AND videos.created_at > '2020.07.27'
GROUP BY videos.id
ORDER BY reactCount DESC")->paginate(16);
Please someone help me to find a solution. I'm really tired man.
Have You tried using whereDate()?
->whereDate('videos.created_at', '>', '2020.07.27');
Or maybe you should use HasMany Relation to get reaction count.
$date = Carbon::createFromFormat('Y-m-d', '2020-07-27');
Create HasMany Relation in Video Model
public function reactions(){
$this->hasMany(Reaction::class, 'at_video', 'id');
}
Create Eloquent Query to fetch record
Video::select('id', 'code', 'title')
->withCount('reactions')
->whereDate('videos.created_at', '>', $date)
->get();
At least I found a solution. This is not the best way maybe but it works without any issues yet.
I added them to my controller
use Illuminate\Pagination\Paginator;
use Illuminate\Database\Eloquent\Collection;
in the function
$query = DB::select(DB::raw("SELECT videos.id AS id, group_concat(DISTINCT videos.code) AS code, group_concat(DISTINCT videos.title) AS title, group_concat(DISTINCT videos.created_at) AS createdDate, count(reactions.id) AS reactCount
FROM videos
LEFT JOIN reactions ON videos.id = reactions.at_video
WHERE (reactions.reaction = 1 or reactions.reaction IS NULL)
AND videos.created_at > '2021.07.27'
GROUP BY videos.id
ORDER BY reactCount DESC"));
// Added this three lines of code to make as the results array as a collection
$collection = new Collection($query);
$page = $request->get('page') ? (int)$request->get('page') : 1;
$videos = $collection->forPage($page, 16);
dd($videos);

Codeigniter's Model with QueryBuilder JOIN query

Really new to working with CI4's Model and struggling to adapt my existing MySQL JOIN queries to work with the examples in its User Guide.
I have adapted part of my code like so:
public function brand_name($brand_name_slug)
{
return $this->asArray()
->where('availability', 'in stock')
->where('sku !=', '')
->where('brand_name_slug', $brand_name_slug)
->groupBy('gtin')
->orderBy('brand_name, subbrand_name, product, size, unit')
->findAll();
}
It works fine. I have looked at examples, and figured out I can add the code ->table('shop a') and it still works, but I also need to to add the following JOIN statement:
JOIN (SELECT gtin, MIN(sale_price) AS sale_price FROM shop GROUP BY gtin) AS b ON a.gtin = b.gtin AND a.sale_price = b.sale_price
As soon as I add ->join('shop b', 'a.gtin = b.gtin and a.sale_price = b.sale_price') I get a '404 - File Not Found' error.
When I look at all examples of CI4 joins and adapt my code to fit, my foreach($shop as $row) loop generates a 'Whoops...' error because they end with a getResult() or getResultArray - instead of findAll().
Which is the way forward, and do I need to change my foreach loop.
Full MySQL statement:
SELECT * FROM shop a JOIN (SELECT gtin, MIN(sale_price) AS sale_price FROM shop GROUP BY gtin) AS b ON a.gtin = b.gtin AND a.sale_price = b.sale_price WHERE availability = 'in stock' AND sku != '' AND brand_name_slug = $brand_name_slug GROUP BY gtin ORDER BY brand_name, subbrand_name, product, size
Query builders have their limits. That's why the query method exists. If you have a complex query I'd advise you to just use $this->query();.
It will make you lose less time and effort converting something you know already works. And in the top of that, while converting complex queries you usually end up using the query builder but with big part of your SQL in it.
In your model extending CodeIgniter\Model :
$query = $this->db->query("SELECT * FROM shop a JOIN (SELECT gtin, MIN(sale_price) AS sale_price FROM shop GROUP BY gtin) AS b ON a.gtin = b.gtin AND a.sale_price = b.sale_price WHERE availability = 'in stock' AND sku != '' AND brand_name_slug = \$brand_name_slug GROUP BY gtin ORDER BY brand_name, subbrand_name, product, size");
// your array result
$result_array = $query->getResultArray();
// your object result
$result_object = $query->getResult();
BaseBuilder Class in Codeigniter expects the first join parameter to be the table name. So try passing the table name and join it on the table name itself. I haven't personally used the table aliases so I might also be wrong.
Following are the parameter that the JOIN query expects :
public function join(string $table, string $cond, string $type = '', bool $escape = null)
Here, it expects the first name be a table, so try out by switching aliases for the table's name directly.
For your second part of query, It would be better if you could show the whole error rather than just posting the first of the error.
Managed to figure it out in the end:
public function brand_name($brand_name_slug)
{
return $this
->db
->table('shop a')
->select()
->join('(SELECT sku, MIN(sale_price) AS sale_price FROM shop GROUP BY sku) AS b', 'a.sku = b.sku AND a.sale_price = b.sale_price')
->where('availability', 'in stock')
->where('a.sku !=', '')
->where('brand_name_slug', $brand_name_slug)
->groupBy('a.sku')
->orderBy('brand_name, subbrand_name, product, size, unit')
->get()
->getResult();
}
Thanks for all your pointers!

leftJoin is returning columns from first table only

The code is in EventUserTypes model
$this->find()
->select(['event_usertypes.user_type_id' , 'usertypes.name'])
->leftJoin('usertypes' , 'event_usertypes.user_type_id = usertypes.id')
->where(['event_usertypes.event_id'=>$event_id])
->all()
There is no error exept that it is only returning the columns of first table
and not the joined table. Its been 2 hours and have waisted too much energy on what is going wrong ? Any idea ?
If I select * then it returns all columns of first table
and if do this
select(['event_usertypes.user_type_id' , 'usertypes.name'])
it only returns event_usertypes.user_type_id not the name from the joined table
Please help me out
Try doing a direct DB query, to ensure that the usertypes table will be available in your query results:
$query = new \yii\db\Query;
$query->select(['e.user_type_id', 'u.name'])
->from('event_usertypes e')
->leftJoin('usertypes u', 'e.user_type_id = u.id')
->where(['e.event_id'=> $event_id]);
$command = $query->createCommand();
$resp = $command->queryAll();
Have a look at this SO question which was similar to yours. Also here is a link to the Yii documentation, in case this might help.
Please try like this
$query = new \yii\db\Query;
$query->select(['event_usertypes.user_type_id' , 'usertypes.name'])
->from('event_usertypes')
->leftJoin('usertypes' , 'event_usertypes.user_type_id = usertypes.id')
->where(['event_id'=> $event_id])->all();
$query->createCommand();

Doctrine Query whereIn and orWhere

My query is this :
$q = Doctrine_Query::create()
->from('plans p')
->whereIn('c.centerid',$centers)
->andWhere('p.agecategory = ?', $ageType)
->andWhere('p.type = ?', '2')
->andWhere('p.active = ?', '1')
->leftJoin('p.plansCenters c');
return $q->execute();
I wont to add one more row :
orWhere('p.allcenters =?' , '1')
Problem is this :
I wont to remove this whereIn clouse if c.allcenters =? 1 and oposit if c.allcenters =? 0 to show query whit whereIn clouse. If i wrhite orWhere after WhereIn clouse this 3 andWhere after this dont work Please help :)
I didn't fully understand the problem but I am familiar with complex orWhere/andWhere clauses. You have to use combined where conditions likes this:
->where("q.column=? AND w.column=?", ...)
->orWhere("e.id=? AND r.name=?", ....)
->orWhere("t.id=? AND (y.id=? OR u.id=?)", ...)
I hope you understand the idea, this is perfectly legit way.