How to group by a single column in Laravel - php

I'm trying to group my results by a selected Raw Field,
$daily_summaries=DB::table("ticket_transactions")
->join('stations', 'stations.id', '=', 'ticket_transactions.station_id')
->join('companies' ,'companies.id','=' ,'ticket_transactions.company_id')
->where(['company_id'=>$id])
->select('ticket_transactions.company_id' , 'companies.name',
DB::raw('LEFT(ticket_transactions.date_issued,10) AS day', 'COUNT(ticket_transactions.id) AS tickets_sold',
'SUM(fare) AS revenue')
)
->groupBy('day')
->orderBy('day DESC')->get();
return $daily_summaries;
this is my sql when i run it direct.
select `ticket_transactions`.`company_id`, `companies`.`name`, LEFT(ticket_transactions.date_issued,10) AS day
from `ticket_transactions`
inner join `stations` on `stations`.`id` = `ticket_transactions`.`station_id`
inner join `companies` on `companies`.`id` = `ticket_transactions`.`company_id`
where (`company_id` = 2) group by `day` order by day DESC
Result

Related

How to find first and last values within query sorted by another column

I am new to coding and this is the most complex query I have tried writing.
I am trying to create a query that will find the first and last entry for meters sorted by bunker_id within a date range. There are 12 different systems that have their meters captured when they are used.
I have several MySQL tables to track usage of systems and component configurations.
One table has a log of meters for all the systems. What I am trying to do is query this table between Date A and Date B, and receive the first and last meter values within the date range for each system. They systems may not be used everyday, but on occasion will have multiple entries in a day.
I am looking to have a query run through POST on a web page with selectors for the days and the system id's. The data will be output into an HTML table.
date
bunker_id
power_on_hours
01-01-2022
A
26115.50
01-02-2022
B
28535.13
01-02-2022
A
26257.38
01-03-2022
B
28682.73
What I am trying to return
bunker_id
starting_meters
ending_meters
A
26115.50
26257.38
B
28535.13
28682.73
The query that I have sets the starting and ending hours as the same value. I tried using MAX and MIN, but everything breaks if someone were to enter 0 for the meter.
SELECT
lu_bunkers.bunker_name as 'bunker_name',
lu_bunkers.bunker_sn,
SUM(system_utilization.hours_used) as 'total_hours',
SUM(CASE WHEN system_utilization.use_id = '1' THEN system_utilization.hours_used ELSE 0 END) as 'maintenance_hours',
SUM(CASE WHEN system_utilization.use_id = '2' THEN system_utilization.hours_used ELSE 0 END) as 'working_rf_hours',
SUM(CASE WHEN system_utilization.use_id = '3' THEN system_utilization.hours_used ELSE 0 END) as 'working_no_rf_hours',
SUM(CASE WHEN system_utilization.use_id = '4' THEN system_utilization.hours_used ELSE 0 END) as 'acd_hours',
((SUM(system_utilization.hours_used))/ ((DATEDIFF('2022-02-24', '2021-01-01')+1)*5/7*12))*100 as net_utilization,
((DATEDIFF('2022-02-24', '2021-01-01')+1)*(5/7)*12) as num_hours,
(SELECT system_meters.power_on_hours WHERE system_utilization.date_used BETWEEN '2021-01-01' AND '2022-02-24' ORDER BY system_utilization.date_used DESC LIMIT 1) as 'ending_hours',
(SELECT system_meters.power_on_hours WHERE system_utilization.date_used BETWEEN '2021-01-01' AND '2022-02-24' ORDER BY system_utilization.date_used ASC LIMIT 1) as 'starting_hours'
FROM system_utilization
LEFT JOIN lu_bunkers ON system_utilization.bunker_id = lu_bunkers.bunker_id
LEFT JOIN lu_use ON system_utilization.use_id = lu_use.use_id
LEFT JOIN system_meters ON system_utilization.id = system_meters.utilization_id
WHERE system_utilization.date_used BETWEEN '2021-01-01' AND '2022-02-24' AND system_utilization.bunker_id LIKE '%'
GROUP BY system_utilization.bunker_id
ORDER BY lu_bunkers.bunker_name
2 possible solutions
Using Joins
SELECT
a.`bunker_id`,
b.`power_on_hours` as `starting_meters`,
c.`power_on_hours` as `ending_meters`
FROM `yourtable` a
LEFT JOIN `yourtable` b
ON (
SELECT `date`
FROM `yourtable`
WHERE `bunker_id` = a.`bunker_id`
ORDER BY `date`
LIMIT 1
) = b.`date`
AND a.`bunker_id` = b.`bunker_id`
LEFT JOIN `yourtable` c
ON (
SELECT `date`
FROM `yourtable`
WHERE `bunker_id` = a.`bunker_id`
ORDER BY `date` DESC
LIMIT 1
) = c.`date`
AND a.`bunker_id` = c.`bunker_id`
GROUP BY a.`bunker_id`
Using subqueries on columns
SELECT
a.`bunker_id`,
(
SELECT `power_on_hours`
FROM `yourtable`
WHERE `bunker_id` = a.`bunker_id`
ORDER BY `date` LIMIT 1
) as `starting_meters`,
(
SELECT `power_on_hours`
FROM `yourtable`
WHERE `bunker_id` = a.`bunker_id`
ORDER BY `date` DESC LIMIT 1
) as `ending_meters`
FROM `yourtable` a
GROUP BY a.`bunker_id`

How to use PDO::FETCH_GROUP with a table join and only returning 3 records in a joined table ordered by date

I am using the following code to get a grouped list of voyage types and their respective voyages.
public function getVoyageTypesWithTrips() {
//query
$this->db->query('
SELECT voyagetype_name
, voyagetype_description
, voyagetype_image
, voyage_id
, voyage_name
, voyage_startDate
FROM voyagetypes
LEFT
JOIN voyages
ON voyagetypes.voyagetype_id = voyages.voyage_type
WHERE voyagetype_deleted != 1
');
//get the results
$results = $this->db->resultSetGrouped();
return $results;
}
//get results set as array of objects - grouped
public function resultSetGrouped() {
$this->execute();
return $this->statement->fetchAll(PDO::FETCH_GROUP);
}
What I want to do is limit the voyages table to only show the 3 most closest records to today instead of returning all the voyages for that type.
So returning
Category 1 (Voyage next week, voyage week after, voyage week after that, no more but loads in table)
Category 2 (voyage tomorrow, no more in this category)
Category 3 (no voyages)
I initially tried ORDER BY and LIMIT but this doesn't work with the PDO::FETCH_GROUP I think.
I believe I need to have my SQL order & limit the joined table prior to sending to the fetch_group??? So something like
$this->db->query('
SELECT voyagetype_name
, voyagetype_description
, voyagetype_image
, voyage_id
, voyage_name
, voyage_startDate
FROM voyagetypes
LEFT
JOIN voyages
ON voyagetypes.voyagetype_id = voyages.voyage_type
APPLY THE SORT AND LIMIT TO THE JOINED TABLE
WHERE voyagetype_deleted != 1
');
One option is to filter with a subquery:
select vt.voyagetype_name, vt.voyagetype_description, vt.voyagetype_image, v.voyage_id, v.voyage_name, v.voyage_startdate
from voyagetypes vt
left join voyages v
on v.voyagetype_id = vt.voyage_type
and (
select count(*)
from voyages v1
where
v1.voyage_type = vt.voyage_type
and v1.voyage_startdate > v.voyage_startdate
) < 3
where vt.voyagetype_deleted <> 1
Or, if you are running MYSQL 8.0, you can just use rank():
select *
from (
select
vt.voyagetype_name,
vt.voyagetype_description,
vt.voyagetype_image,
v.voyage_id,
v.voyage_name,
v.voyage_startdate,
rank() over(partition by vt.voyage_type order by v.voyage_startdate desc) rn
from voyagetypes vt
left join voyages v on v.voyagetype_id = vt.voyage_type
where vt.voyagetype_deleted <> 1
) t
where rn <= 3

Yii2 join a subquery

I'm building a booking site, where properties (ex: hotel) have rooms and you can book them.
I've made a raw SQL query which filters the available rooms in a specified date range, but I don't know how to implement it in a YII AR way: using with active record find() as a relation
Do you guys have any suggestion or opinion about this?
SELECT
property.id,
property_lang.name,
max_people.total
FROM property
JOIN property_lang ON (property.id = property_lang.property_id AND property_lang.lang_id = 'hu')
JOIN (
SELECT
property_id AS id,
sum(max_people) AS total
FROM room
WHERE room.id NOT IN (
SELECT room_id
FROM booking
JOIN booking_status ON (booking.last_status_id = booking_status.id)
JOIN booking_room ON (booking.id = booking_room.id)
JOIN property ON booking.property_id = property.id
WHERE (booking_status.status > -1 OR booking_status.status IS NULL)
AND booking.check_in < '2017-10-18'
AND booking.check_out > '2017-10-14'
)
GROUP BY property_id
) max_people
ON max_people.id = property_id
ORDER BY property.id
The simplest solution is use the createCommand with params:
\Yii::$app->db->createCommand("SELECT
property.id,
property_lang.name,
max_people.total
FROM property
JOIN property_lang ON (property.id = property_lang.property_id AND property_lang.lang_id = 'hu')
JOIN (
SELECT
property_id AS id,
sum(max_people) AS total
FROM room
WHERE room.id NOT IN (
SELECT room_id
FROM booking
JOIN booking_status ON (booking.last_status_id = booking_status.id)
JOIN booking_room ON (booking.id = booking_room.id)
JOIN property ON booking.property_id = property.id
WHERE (booking_status.status > -1 OR booking_status.status IS NULL)
AND booking.check_in < ':startdate'
AND booking.check_out > ':finishdate'
)
GROUP BY property_id
) max_people
ON max_people.id = property_id
ORDER BY property.id")
->bindValues([':startdate' => $startDate, ':finishdate' => $startDate])
->execute();
Other way is to split query for subqueries and try to use AR. It will be something like that:
$subSubQuery=Booking::find()
->select(['room_id'])
->join('booking_status', 'booking.last_status_id = booking_status.id')
->join('booking_room', 'booking.id = booking_status.id')
->join('property', 'booking.property_id= booking_propery.id')
->andWhere(['or',
['>','booking_status.status',-1],
[ 'booking_status.status'=>null],
])
->andWhere(['<','booking.check_in',$startDate])
->andWhere(['>','booking.checkout',$finishDate])
->groupBy('property_id');
$subQuery = Room::find()->select(['property_id as id','sum(max_people) as total'])->andFilterWhere(['not in', 'answers_metering.id', $subSubQuery]);
$query = Property::find()->select(['property.id','property_lang.name','max_people.total'])
->join($subQuery. ' as max_people', 'max_people.id = property.id')
->join('property_lang', 'property.id = property_lang.property_id AND property_lang.lang_id = \'hu\'')
->orderBy(['property.id' => SORT_ASC])
->all();

mysql select equal data with same date wise

I'm trying to run a mysql join query fetching data from 3 tables.
Table 1: user
Table 2: Outtiming
Table 3: Intiming
I want to show same date data from outtiming and intiming tables with same dates.
e.g: 2017-09-13 = 2017-09-13
But I'm not getting data with same dates.
$query = "SELECT ur.username, ur.user_department, it.*, ot.*
FROM users ur
INNER JOIN intiming it ON ur.staff_id=it.staff_id
INNER JOIN outtiming ot ON ur.staff_id=ot.staff_id
WHERE it.staff_id=".$employee."
AND it.date >= '$startDate' AND ot.date <= '$endDate'";
Result
mysql query result
Add an addition condition in your WHERE statement to check matching dates: it.date = ot.date . Full query:
$query = "SELECT ur.username, ur.user_department, it.*, ot.*
FROM users ur
INNER JOIN intiming it ON ur.staff_id=it.staff_id
INNER JOIN outtiming ot ON ur.staff_id=ot.staff_id
WHERE it.staff_id=".$employee."
AND it.date >= '$startDate' AND ot.date <= '$endDate'
AND it.date = ot.date ";

SQL search Result, Group by ID number

I'm having some issues with trying to fix this SQL Query
This is a custom search query which is searching for the word 'weddings' on all pages on this CMS system.
At the moment I am getting the same page appear on the first 5 rows because the word 'weddings' appears 5 times. What I want to do is combine the rows with the same ID number into 1 row so it doesn't appear multiple times.
I thought doing a group by at the end of this statement would do this but I keep getting an SQL syntax error
GROUP BY `documents`.`id`
I have attached the full SQL bellow with an image of the output i currently get.... Any idea?
SELECT `documents`.*,
`documenttypes`.`name` as `doctype`,
`articles`.`id` as `article_id`,
`articles`.`language_id`,
`articles`.`title`,
`articles`.`template`,
`articles`.`slug`,
`articles`.`path`,
`articles`.`slug_title`,
MATCH ( elements.textvalue )AGAINST ( 'weddings' ) AS score,
elements.textvalue AS matching,
LOWER(`articles`.`title`)
LIKE '%weddings%' as 'like_title',
( MATCH ( elements.textvalue )
AGAINST ( 'weddings' ) ) + IF(( LOWER(`articles`.`title`)
LIKE '%weddings%'),1, 0) + IF((LOWER(`elements`.`textvalue`)
LIKE '%weddings%'),1, 0) as total FROM (`documents`)
LEFT JOIN `articles` ON `articles`.`document_id` = `documents`.`id`
LEFT JOIN `documenttypes` ON `documents`.`documenttype_id` = `documenttypes`.`id`
LEFT JOIN `documents_users` AS du ON `documents`.`id` = du.`document_id`
LEFT JOIN `documents_usergroups` AS dug ON `documents`.`id` = dug.`document_id`
LEFT JOIN elements ON `elements`.`article_id` = `articles`.`id`
WHERE `documents`.`trashed` = 0
AND `documents`.`published` = 1
AND `articles`.`status_id` = 1
AND `articles`.`language_id` = 1
AND (`documents`.`no_search` = '0'
OR `documents`.`no_search` IS NULL)
AND ( (dug.usergroup_id IS NULL)
AND (du.user_id IS NULL) )
AND (`documents`.`startdate` < NOW()
OR `documents`.`startdate` = '0000-00-00 00:00:00' OR `documents`.`startdate` IS NULL)
AND (`documents`.`enddate` > NOW()
OR `documents`.`enddate` = '0000-00-00 00:00:00'
OR `documents`.`enddate` IS NULL)
HAVING (total > 0)
ORDER BY label ASC,
total DESC LIMIT 0,10
You can try to use the statement DISTINCT:
SELECT DISTINCT 'documents'.*,
'documenttypes'.'name' as 'doctype',
'articles'.'id' as 'article_id',
...
GROUP BY lets you use aggregate functions, like AVG, MAX, MIN, SUM, and COUNT which apparently you don't use.

Categories