Query Condition if nothing add 0 - laravel

I have a Laravel 5.7 query
$captains = DB::table('pickup_results')
->select(DB::raw("playerID"),
DB::raw("COUNT(CASE WHEN gameResult = 'Win' THEN 1 END) AS wins"),
DB::raw("COUNT(CASE WHEN gameResult = 'Loss' THEN 1 END) AS loss"))
->where('pickOrder', '=', 0)
->where('playerID', '=', $playerID)
->groupBy('playerID')
->orderBy('wins','DESC')
->get();
If the playerID doesn't have a "Win" or a "Loss" it wont add the key of wins or loss to the query results -- thus resulting in errors when a user has no wins or no loss's in the database.
I Would like to be able to default wins or loss to 0 when a value doesn't exist in the database.
Can someone help me understand how I would achieve?

Assuming that there isn't going to be a row in the table if the player hasn't had a win or a loss then your query isn't ever going to return any results.
One option (assuming you have a players table) would be to query the players table and then left join the pickup_results:
$captains = DB::table('players')
->leftJoin('pickup_results', 'players.id', 'pickup_results.playerID')
->select('users.id as user_id',
DB::raw("COUNT(CASE WHEN results.gameResult = 'Win' THEN 1 END) AS wins"),
DB::raw("COUNT(CASE WHEN results.gameResult = 'Lose' THEN 1 END) AS loss"))
->where('players.id', $playerID)
->where('pickup_results.pickOrder', 0)
->groupBy('players.id')
->orderBy('wins', 'DESC')
->get();
Alternatively, since you're using get() with your query a collection will be returned regardless of any results so you could chain on the whenEmpty() method instead:
$captains = DB::table('pickup_results')
->select('playerID',
DB::raw("COUNT(CASE WHEN gameResult = 'Win' THEN 1 END) AS wins"),
DB::raw("COUNT(CASE WHEN gameResult = 'Loss' THEN 1 END) AS loss"))
->where('pickOrder', 0)
->where('playerID', $playerID)
->groupBy('playerID')
->orderBy('wins', 'DESC')
->get()
->whenEmpty(function ($items) use ($playerID) {
return $items->push((object)[
'playerID' => $playerID,
'wins' => 0,
'loss' => 0,
]);
});

You can use SUM() instead of COUNT() and add an ELSE 0 to the query. Example for the first occurance:
DB::raw("SUM(CASE WHEN gameResult = 'Win' THEN 1 ELSE 0 END) AS wins")
Or you can use COALESCE(query, 0):
DB::raw("COALESCE(COUNT(CASE WHEN gameResult = 'Win' THEN 1 END), 0) AS wins")
This works because COUNT() returns NULL if all counted elements are NULL.

It will give you wins and loss in your result. Just add an else condition in your case and put an extra count condition for draw as well. Add more than 1 orderBy for better representation of results.
$captains = DB::table('pickup_results')
->select(DB::raw("playerID"),
DB::raw("SUM(CASE WHEN gameResult = 'Win' THEN 1 ELSE 0 END) AS wins"),
DB::raw("SUM(CASE WHEN gameResult = 'Loss' THEN 1 ELSE 0 END) AS loss")),
DB::raw("SUM(CASE WHEN gameResult != 'Win' and gameResult != 'Loss' THEN 1 ELSE 0 END) AS draws"))
->where('pickOrder', '=', 0)
->where('playerID', '=', $playerID)
->groupBy('playerID')
->orderBy('wins','DESC')
->orderBy('draws','DESC')
->orderBy('loss','ASC')
->get();

Related

how to make orderBy ignore value 0 in eloquent laravel

My problem is that I have my collection that has a position field and I want it to be sorted in ascending order, but the fields that have the value null or 0 by default are detected as smaller than those that have an index.
My question is how can I make orderBy ignore the value 0 or null.
$listing = Product::get();
$listing = $listing->orderBy('order','ASC');
You could use CASE in your orderBy as a hack to "ignore" 0 (place it last).
$listing = Product::query()
->orderByRaw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END DESC, "order" ASC')
->get();
You can also split it if you prefer.
$listing = Product::query()
->orderByRaw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END DESC')
->orderBy('order')
->get();
$listing = Product::query()
->orderByDesc(DB::raw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END'))
->orderBy('order')
->get();
$listing = Product::orderByRaw('-order DESC')->get();
There is a minus sign before the column.
Instead of asc, we are now sorting as desc, this is because we have inverted the values in #2 and so the sorting must also be inverted now to get the right results.
this working fine for me.

How to take count based on different where conditions in laravel?

I am trying to count tickets based on different where conditions . For which I am using four different queries but same model. Can I do that in one query?
$openTickets = Ticket::where('status',1)->count();
$pending = Ticket::where('status',2)->count();
$unAssigned = Ticket::where('agent_id',null)->count();
$unResolved = Ticket::whereNotIn('status',[3,4])->count();
Ticket::selectRaw('COUNT(CASE WHEN status = 1 THEN 1 END) AS open_tickets')
->selectRaw('COUNT(CASE WHEN status = 2 THEN 1 END) AS pending_tickets')
->selectRaw('COUNT(CASE WHEN agent_id IS NULL THEN 1 END) AS unassigned_tickets')
->selectRaw('COUNT(CASE WHEN status NOT IN (3,4) THEN 1 END) AS unresolved_tickets')
->first();
You can of course resolve the multiple queries with this query. We can use conditional cases and count.
You can sum up conditionals but will need lots of raw parts in your query:
$result = DB::table('tickets')->whereIn('status', [1,2])->orWhereNull('agent_id')->orWhereNotIn('status', [3,4]) // This is to filter out the things we don't care about
->select([
DB::raw('SUM(IF(status = 1, 1,0)) as countOpen'),
DB::raw('SUM(IF(status = 2, 1,0)) as countPending'),
DB::raw('SUM(IF(agent_id IS NULL, 1,0)) as countUnassigned'),
DB::raw('SUM(IF(status NOT IN (3,4), 1,0)) as countUnresolved'),
])->first()
$openTickets = $result->countOpen;
$pending = $result->countPending;
$unAssigned = $result->countUnassigned;
$unResolved = $result->countUnresolved;

Laravel - Display Active and Inactive on the same page

I have a table with these fields: name and plan_status.
The plan_status can be 0 or 1. 1 is active, 0 is inactive. I want to display |name|active|inactive|.
I am only able to run a query that displays either active and inactive ones.
Model:
protected $fillable = [
'name', 'plan_status'
];
Controller:
$actives = DB::table('users')
->select(DB::raw('count(*) as active'))
->where('plan_status', 1)
I want the query to display both names, active and inactive: |name|active|inactive|.
Here is the query which will provide you exact result which you want
$actives = DB::table('users')
->selectRaw('
name,
IF((plan_status = 1), "Yes", "No") as active,
IF((plan_status = 0), "Yes", "No") as inactive,
IF((plan_status = 2), "Yes", "No") as undefined
')
->get()
Try this and check. I think this will solve your problem.
Edited
To get count of status wise
$actives = DB::table('users')
->selectRaw('
name,
IF((plan_status = 1), COUNT(*), "0") as active,
IF((plan_status = 0), COUNT(*), "0") as inactive,
IF((plan_status = 2), COUNT(*), "0") as undefined
')
->groupBy('plan_status')
->get()
like this
Try both the solutions.
try this one using group by
DB::table('users')
->select('name',
DB::raw('IF((plan_status = 1), count(*), 0) as active'),
DB::raw('IF((plan_status = 0), count(*), 0) as inactive'),
)->groupBy('plan_status')->get()

Laravel Eloquent counts with multiple ranges of data

I'm trying to figure out how to create Laravel query that will produce me with the following mysql query.
select
count(CASE WHEN points BETWEEN 0 AND 1.99 THEN 1 END) as count0_199,
count(CASE WHEN points BETWEEN 2 AND 3.99 THEN 1 END) as count2_399,
count(CASE WHEN points BETWEEN 4 AND 5.99 THEN 1 END) as count4_599
from
stats
Is it possible to make it in Eloquent? Haven't seen it in the documentation.
Thanks
From my knowledge using eloquent to perform this will involve some formatting of returned data:
$results = [];
$stats = Stat::where('points', '>=', '0')
->where('points', '<=', 5.99)
->get()
->each(function ($stat, $key) use (&$results) {
if ($stat->points >= 0 && $stat->points <= 1.99) {
$results['count0_199'][] = $stat;
}
if ($stat->points >= 2 && $stat->points <= 3.99) {
$results['count2_399'][] = $stat;
}
if ($stat->points >= 4 && $stat->points <= 5.99) {
$results['count4_599'][] = $stat;
}
});
return $results;
In the code above, all stats where points are between 0 and 5.99 are returned (as they will be returned anyway). The returned collection is then looped through to populate a $results array which will group the returned data.
There is also the DB Facade:
$query = [
'count(CASE WHEN points BETWEEN 0 AND 1.99 THEN 1 END) as count0_199',
'count(CASE WHEN points BETWEEN 2 AND 3.99 THEN 1 END) as count2_399',
'count(CASE WHEN points BETWEEN 4 AND 5.99 THEN 1 END) as count4_599'
];
$results = DB::select(implode(',', $query) . ' from stats');

Refactor Laravel Query

I have a query that I have built, and I am trying to understand how I can achieve the same thing but in one single query. I am fairly new to Laravel and learning. Anyway someone could help me understand how I can achieve what I am after?
$activePlayerRoster = array();
$pickupGames = DB::table('pickup_games')
->where('pickupDate', '>=', Carbon::now()->subDays(30)->format('m/d/Y'))
->orderBy('pickupDate', 'ASC')
->get();
foreach ($pickupGames as $games) {
foreach(DB::table('pickup_results')
->where('pickupRecordLocatorID', $games->recordLocatorID)
->get() as $activePlayers) {
$activePlayerRoster[] = $activePlayers->playerID;
$unique = array_unique($activePlayerRoster);
}
}
$activePlayerList = array();
foreach($unique as $playerID) {
$playerinfo = DB::table('players')
->select('player_name')
->where('player_id', $playerID)
->first();
$activePlayerList[] = $playerinfo;
}
return $activePlayerList;
pickup_games
checkSumID
pickupDate
startTime
endTime
gameDuration
winningTeam
recordLocatorID
pickupID
1546329808471
01/01/2019
08:03 am
08:53 am
50 Minute
2
f47ac0fc775cb5793-0a8a0-ad4789d4
216
pickup_results
id
checkSumID
playerID
team
gameResult
pickOrder
pickupRecordLocatorID
1
1535074728532
425336395712954388
1
Loss
0
be3532dbb7fee8bde-2213c-5c5ce710
First, you should try to write SQL query, and then convert it to Laravel's database code.
If performance is not critical for you, then it could be done in one query like this:
SELECT DISTINCT players.player_name FROM pickup_results
LEFT JOIN players ON players.player_id = pickup_results.playerID
WHERE EXISTS (
SELECT 1 FROM pickup_games
WHERE pickupDate >= DATE_FORMAT(SUBDATE(NOW(), INTERVAL 30 DAY), '%m/%d/%Y')
AND pickup_results.pickupRecordLocatorID = recordLocatorID
)
Here I'm assuming you know what you're doing with this dates comparison, because it looks weird to me.
Now, let's convert it to Laravel's code:
DB::table('pickup_results')
->select('players.player_name')->distinct()
->leftJoin('players', 'players.player_id', '=', 'pickup_results.playerID')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pickup_games')
->where('pickupDate', '>=', Carbon::now()->subDays(30)->format('m/d/Y'))
->whereRaw('pickup_results.pickupRecordLocatorID = recordLocatorID');
})
->get();
Basically, I would reduce the query to its SQL variant to get directly at its core.
The essence of the query is
select `x` FROM foo WHERE id IN (
select distinct bar.id from bar join baz on bar.id = baz.id);
This can be interpreted in Eloquent as:
$thirtyDaysAgo = Carbon::now()->subDays(30)->format('m/d/Y');
$playerIds = DB::table('pickup_games')
->select('pickup_games.player_id')
->join(
'pickup_results',
'pickup_results.pickupRecordLocatorID',
'pickup_games.recordLocatorID')
->where('pickupDate', '>=', $thirtyDaysAgo)
->orderBy('pickupDate', 'ASC')
->distinct('pickup_games.player_id');
$activePlayers = DB::table('players')
->select('player_name')
->whereIn('player_id', $playerIds);
//>>>$activePlayers->toSql();
//select "player_name" from "players" where "player_id" in (
// select distinct * from "pickup_games"
// inner join "pickup_results"
// on "pickup_results"."pickupRecordLocatorID" = "pickup_games"."recordLocatorID"
// where "pickupDate" >= ? order by "pickupDate" asc
//)
From the resulting query, it may be better to refactor the join as relationship between the Eloquent model for pickup_games and pickup_results. This will help to further simplify $playerIds.

Resources