MySQL - Group by for UNION query - php

I've been trying to find a solution to my problem for a couple of hours now and can't come up with the right query. I have two tables, stocktake_scans and stocktake_items. I need to select data from both tables and group them together. The query that I have at the moment is this:
SELECT
a.department_description, a.sub_department_description, a.total_cost, a.total_retail, SUM(a.qty) AS qty, a.total_vat, a.vat_rate
FROM (
SELECT (CASE WHEN TRIM(IFNULL(sp.department_description, '')) = '' THEN 'N/A' ELSE sp.department_description END) AS department_description,
(CASE WHEN TRIM(IFNULL(sp.sub_department_description, '')) = '' THEN 'N/A' ELSE sp.sub_department_description END) AS sub_department_description,
SUM(sp.unit_cost_price * ss.quantity) AS total_cost,
SUM(sp.unit_retail_price * ss.quantity) AS total_retail,
sum(ss.quantity) AS qty,
(SUM(sp.unit_cost_price*ss.quantity)) * (sv.vat_rate/100) AS total_vat,
sv.vat_rate as vat_rate
FROM
stocktake_scans ss
INNER JOIN stocktake_products sp ON ss.stocktake_product_id = sp.stocktake_product_id
LEFT JOIN stocktake_vat_codes sv ON sv.vat_code = sp.vat_code AND sv.stocktake_id = '5778'
WHERE ss.stocktake_id = '5778'
GROUP BY sp.department_description, sp.sub_department_description
UNION ALL
SELECT (CASE WHEN TRIM(IFNULL(si.department_description, '')) = '' THEN 'N/A' ELSE si.department_description END) AS department_description,
'N/A' as sub_department_description,
SUM(si.unit_cost_price * si.quantity) AS total_cost,
SUM(si.unit_retail_price * si.quantity) AS total_retail,
sum(si.quantity) AS qty,
SUM(si.unit_cost_price * quantity)*(sv.vat_rate/100) AS total_vat,
sv.vat_rate as vat_rate
FROM stocktake_items si
LEFT JOIN stocktake_vat_codes sv ON sv.vat_code = si.vat_code AND sv.stocktake_id = '5778'
WHERE si.stocktake_id = '5778' GROUP BY si.department_description ) a
GROUP
BY a.department_description, a.sub_department_description
This doesn't fully do the job. I get all the data from stocktake_scans, followed by data from stocktake_items.
I get the below as the first result
And then at the end of the excel file I also get this
What is the right way of doing this, so that both Alcohol figures are displayed in one place?

Add ORDER BY a.department_description, a.sub_department_description before the last GROUP BY

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

mysql datatable.net totalling of a rows values not working correctly

Using Mysql I need to calculate the total of values on a given row, my attempts to do this have failed with unusual results, one row working and others showing no total data. This call is in reference to a previous call answered by scaisEdge, with thanks.
mysql how to display values on one row for each portfolio, stock
I am getting values in the output but only for one row, so it is not behaving as I would expect.
$rows = $wpdb->get_results("
select
t.Symbol AS Symbol
, t.Portfolio AS Portfolio
, max(Janx) AS Janx
, max(Febx) AS Febx
, max(Marx) AS Marx
... etc.
, max(Decx) AS Decx
, (
max(Janx)
+ max(Febx)
+ max(Decx)
) AS Total
from (
SELECT
s.symbol AS Symbol,
p.code AS Portfolio,
(case when ANY_VALUE(DATE_FORMAT(d.pdate,'%b')) = 'Jan' then
IFNULL(CEILING((SUM(IF(t.transaction_type='Buy',t.quantity,t.quantity
* -1)) * ANY_VALUE(d.dividend) ) / 100),0) end) AS Janx,
(case when ANY_VALUE(DATE_FORMAT(d.pdate,'%b')) = 'Feb' then
IFNULL(CEILING((SUM(IF(t.transaction_type='Buy',t.quantity,t.quantity *
-1)) * ANY_VALUE(d.dividend) ) / 100),0) end) AS Febx,
(case when ANY_VALUE(DATE_FORMAT(d.pdate,'%b')) = 'Mar' then
IFNULL(CEILING((SUM(IF(t.transaction_type='Buy',t.quantity,t.quantity * -1)) * ANY_VALUE(d.dividend) ) / 100),0) end) AS Marx,
... etc
(case when ANY_VALUE(DATE_FORMAT(d.pdate,'%b')) = 'Dec' then
IFNULL(CEILING((SUM(IF(t.transaction_type='Buy',t.quantity,t.quantity * -1)) * ANY_VALUE(d.dividend) ) / 100),0) end) AS Decx
FROM dm_transactions t
INNER JOIN dm_dividends d ON (d.stock_id = t.stock_id)
INNER JOIN dm_stocks s ON (s.id = t.stock_id)
INNER JOIN dm_portfolios p ON (t.portfolio_id = p.id)
WHERE ANY_VALUE(d.pdate) >= CURDATE()
AND
t.user_id >= IF($user_id=4,2,$user_id)
AND
t.user_id <= IF($user_id=4,3,$user_id)
GROUP BY s.symbol,d.pdate
) t
GROUP BY t.Symbol, t.Portfolio
... etc.
foreach ($rows as $row ){
echo "<tr>";
echo "<td>$row->Portfolio</td>";
echo "<td>$row->Symbol</td>";
echo "<td>$row->Janx</td>";
... etc.
echo "<td>$row->Decx</td>";
echo "<td>$row->Total</td>";
The total values are not summating correctly, some rows show no totals values, when there is data, my SQL query must be incorrect I suspect. All other aspects of my report are working.
Hopefully a simple error on my part.
Many Thanks in advance
Colin
My IFNULL expression was not positioned in the MySQL statement correctly, the revised IFNULL statement is shown below
SELECT
ANY_VALUE(s.symbol) AS Symbol,
ANY_VALUE(p.code) AS Portfolio,
IFNULL((case when ANY_VALUE(DATE_FORMAT(d.pdate,'%b')) = 'Jan' then CEILING((SUM(IF(t.transaction_type='Buy',t.quantity,t.quantity * -1)) * ANY_VALUE(d.dividend) ) / 100) end),0) AS Janx,
IFNULL((case when ANY_VALUE(DATE_FORMAT(d.pdate,'%b')) = 'Feb' then CEILING((SUM(IF(t.transaction_type='Buy',t.quantity,t.quantity * -1)) * ANY_VALUE(d.dividend) ) / 100) end),0) AS Febx,
Thanks Colin

Trying to get multiple results with nested select statements

I'm trying to get this statement to return multiple rows (one for each campaign ID). This code was originally intended for only accepting one campaign ID, but I'm needing to involve it to accepting multiple campaign IDs. The following code snippet only returns 1 row, but should be returning 6 rows. I'd also assume this statement could be more efficient.
SELECT * FROM
(SELECT
campaignID,
count(ctaS.engagementID) AS numEngagements,
count(distinct(ctaS.targetID)) AS numTargets,
SUM(engagementType='email') AS numEmails,
SUM(engagementType='call') as numCalls,
SUM(engagementType='meeting') as numMeetings,
count(distinct(ctaC.email)) AS numEngagementsByEmail,
count(distinct(ctaS.ipAddress)) AS numEngagementsByIP
FROM engagements AS ctaS
LEFT JOIN engagements_content AS ctaC ON ctaS.engagementID = ctaC.engagementID
WHERE ctaS.campaignID IN ('639ba3c9d86726f2ef5a6ca3','2bce996549ed7acb','f91491361140ccd81a7ab8bb','3d4d9a45bba4b8ef9c22edbc','VhFfwsAnxr9OBfRvGWKsyr7H','0819c9ff210c24f9efcb16fc')
AND ctaS.is_active = 1
AND ctaS.targetID <> ''
AND ctaS.engagementType <> ''
AND ctaS.ipAddress NOT IN ('75.43.48.211')
) as tbl1,
(SELECT
t1.numTimesIndexLoaded,
t1.numTimesIndexLoadedUnique,
t1.numTimesResultsLoaded,
t1.numTimesResultsLoadedUnique,
t1.numTimesCTALoaded,
t1.numTimesCTALoadedUnique
FROM
(SELECT sum(ctaSL.page = 'index') AS numTimesIndexLoaded,
sum(ctaSL.page = 'results') AS numTimesResultsLoaded,
sum(ctaSL.page = 'cta') AS numTimesCTALoaded,
count(DISTINCT CASE WHEN ctaSL.page = 'index' THEN ctaSL.ip_address END) AS numTimesIndexLoadedUnique,
count(DISTINCT CASE WHEN ctaSL.page = 'results' THEN ctaSL.ip_address END) AS numTimesResultsLoadedUnique,
count(DISTINCT CASE WHEN ctaSL.page = 'cta' THEN ctaSL.ip_address END) AS numTimesCTALoadedUnique
FROM stats_page_loads AS ctaSL
WHERE ctaSL.campaign_id IN ('639ba3c9d86726f2ef5a6ca3','2bce996549ed7acb','f91491361140ccd81a7ab8bb','3d4d9a45bba4b8ef9c22edbc','VhFfwsAnxr9OBfRvGWKsyr7H','0819c9ff210c24f9efcb16fc')
AND ctaSL.is_active = 1
AND ctaSL.ip_address NOT IN ('75.43.48.211')
) t1
) as tbl2,
(SELECT tblA.numFacebookClicks, tblB.numTwitterClicks
FROM
(SELECT count(target_id) AS numFacebookClicks
FROM stats_social_clicks AS sc
WHERE sc.social_network = 'facebook'
AND sc.campaign_id IN ('639ba3c9d86726f2ef5a6ca3','2bce996549ed7acb','f91491361140ccd81a7ab8bb','3d4d9a45bba4b8ef9c22edbc','VhFfwsAnxr9OBfRvGWKsyr7H','0819c9ff210c24f9efcb16fc')
AND sc.is_active = 1
) AS tblA
JOIN
(SELECT count(target_id) AS numTwitterClicks
FROM stats_social_clicks AS sc
WHERE sc.social_network = 'twitter'
AND sc.campaign_id IN ('639ba3c9d86726f2ef5a6ca3','2bce996549ed7acb','f91491361140ccd81a7ab8bb','3d4d9a45bba4b8ef9c22edbc','VhFfwsAnxr9OBfRvGWKsyr7H','0819c9ff210c24f9efcb16fc')
AND sc.is_active = 1
) AS tblB
) as tbl3
WHERE campaignID IN ('639ba3c9d86726f2ef5a6ca3','2bce996549ed7acb','f91491361140ccd81a7ab8bb','3d4d9a45bba4b8ef9c22edbc','VhFfwsAnxr9OBfRvGWKsyr7H','0819c9ff210c24f9efcb16fc')

group by months and display chronologically and not alphabetically in mysql

Although the query returns the result but the group by Month returns result alphabetically like April,August,June ... but it should return result Chronologically like Jan ,Feb,....
SELECT Concat(Monthname(a.fdate), '-', Year(a.fdate)) AS Month,
s.new_state AS State,
d.new_dist AS District,
b.ifbook AS Book,
Sum(a.amt) AS Amount
FROM str a
JOIN sc b
ON b.scd = a.isc
JOIN USER c
ON a.ed = c.str
JOIN state_mapping s
ON b.state = s.org_state
JOIN dist_mapping d
ON b.dist = d.org_dist
WHERE Trim(b.ifbook) <> ''
AND b.ifbook IS NOT NULL
AND b.ifbook NOT LIKE '%TR%'
AND Trim(d.new_dist) <> ''
AND d.new_dist IS NOT NULL
GROUP BY b.ifbank,
d.new_dist,
s.new_state,
month
How can I modify the query to achieve the same?
Without knowing anything about your model I could suggest you to give this a shot:
select
concat(monthname(a.fdate),'-',year(a.fdate)) Month,
s.new_state State,
d.new_dist District,
b.ifbook Book,
sum(a.amt) Amount
from str a
JOIN sc b on b.scd = a.isc
JOIN user c on a.ed = c.str
JOIN state_mapping s on b.state = s.org_state
JOIN dist_mapping d on b.dist = d.org_dist
where TRIM(b.ifbook) <> '' and b.ifbook IS NOT NULL and b.ifbook not like '%TR%'
and TRIM(d.new_dist) <> '' and d.new_dist IS NOT NULL
group by b.ifbank, d.new_dist, s.new_state, Month
order by year(a.fdate), month(a.fdate)

Categories