Group concat data values by year - php

I have a query that will sum total loss of data by month within a current year.
SELECT label, m, coalesce(sum(total_loss), 0) as data
FROM (
SELECT label, m
FROM
(
SELECT YEAR(CURDATE()) label UNION ALL SELECT YEAR(CURDATE())-1
) years,
(
SELECT 01 m UNION ALL SELECT 02 UNION ALL SELECT 03 UNION ALL SELECT 04
UNION ALL SELECT 05 UNION ALL SELECT 06 UNION ALL SELECT 07 UNION ALL SELECT 08
UNION ALL SELECT 09 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
) months
) ym
LEFT JOIN incident ON ym.label = YEAR(incident.incident_date) AND ym.m = MONTH(incident.incident_date)
GROUP BY label, m
The data output is
Array
(
[0] => Array
(
[label] => 2017
[m] => 1
[data] => 0
)
[1] => Array
(
[label] => 2017
[m] => 2
[data] => 0
)
[2] => Array
(
[label] => 2017
[m] => 3
[data] => 0
)
[3] => Array
(
[label] => 2017
[m] => 4
[data] => 0
) etc..
This will output all 12 months, and data does not exist will be 0.
What I'm looking for is some sort of group concat that will output something like this:
{
data: [0, 0, 0, 0, 0, 1100, 0, 0, 0, 0, 0, 0],
label: '2017'},
Is it possible to adjust with group concat or write a for each loop to create the data above?

Try grouping only by year, and then use GROUP_CONCAT on the sums across all months:
SELECT
label,
GROUP_CONCAT(COALESCE(SUM(total_loss), 0)) AS data
FROM
(
SELECT label, m
FROM
(
SELECT YEAR(CURDATE()) label UNION ALL SELECT YEAR(CURDATE())-1
) years,
(
SELECT 01 m UNION ALL SELECT 02 UNION ALL SELECT 03 UNION ALL SELECT 04
UNION ALL SELECT 05 UNION ALL SELECT 06 UNION ALL SELECT 07 UNION ALL SELECT 08
UNION ALL SELECT 09 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
) months
) ym
LEFT JOIN incident
ON ym.label = YEAR(incident.incident_date) AND
ym.m = MONTH(incident.incident_date)
GROUP BY label;

Related

php group array results in single lines

Not sure if the title is appropriate for the question, but i have the following problem.
The following code:
while($row = mysql_fetch_array($result2)) {
print_r($row);
}
Produces the following result:
Array
(
[y] => 2016
[m] => 9
[status] => 0
[count] => 2
)
Array
(
[y] => 2016
[m] => 9
[status] => 1
[count] => 33
)
Array
(
[y] => 2016
[m] => 9
[status] => 2
[count] => 4
)
Array
(
[y] => 2016
[m] => 10
[status] => 0
[count] => 20
)
Array
(
[y] => 2016
[m] => 10
[status] => 1
[count] => 3
)
Array
(
[y] => 2016
[m] => 10
[status] => 2
[count] => 14
)
I would like some help with a function that would convert these results to the following format:
Array
(
[y] => 2016
[m] => 9
[<name status 0>] => 2
[<name status 1>] => 33
[<name status 2>] => 4
)
Array
(
[y] => 2016
[m] => 10
[<name status 0>] => 20
[<name status 1>] => 3
[<name status 2>] => 14
)
Any help is greatly appreciated.
This is my first post, so if this post misses important information .. please point it out, and i will add them.
Thank you in advance.
Edit:
The Mysql query:
SELECT y, m, count(status) as count, status
FROM (
SELECT y, m
FROM
(SELECT YEAR(CURDATE()) y UNION ALL SELECT YEAR(CURDATE())-1) years,
(SELECT 1 m UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) months) ym
LEFT JOIN db_orders
ON ym.y = YEAR(db_orders.delivery_date)
AND ym.m = MONTH(db_orders.delivery_date)
WHERE
(y=YEAR(CURDATE()) AND m<=MONTH(CURDATE()))
OR
(y<YEAR(CURDATE()) AND m>MONTH(CURDATE()))
GROUP BY y, m, status;
Edit 2:
The solution:
editing the select for the mysql query solved my problem:
SELECT y, m, COUNT(IF(status='0',1, NULL)) '', COUNT(IF(status='1',1, NULL)) '', COUNT(IF(status='2',1, NULL)) ''
This leads to the following query:
SELECT y, m, COUNT(IF(status='0',1, NULL)) '<name for status 0>', COUNT(IF(status='1',1, NULL)) '<name for status 1>', COUNT(IF(status='2',1, NULL)) '<name for status 2>'
FROM (
SELECT y, m
FROM
(SELECT YEAR(CURDATE()) y UNION ALL SELECT YEAR(CURDATE())-1) years,
(SELECT 1 m UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) months) ym
LEFT JOIN db_orders
ON ym.y = YEAR(db_orders.delivery_date)
AND ym.m = MONTH(db_orders.delivery_date)
WHERE
(y=YEAR(CURDATE()) AND m<=MONTH(CURDATE()))
OR
(y<YEAR(CURDATE()) AND m>MONTH(CURDATE()))
GROUP BY y, m, status;

Php, MySql join tables, limit records per category

I have two tables:
Items
ID Name Model_ID
---------------------------------
1 2010 Audi L1 1
2 2014 BMW X2 2
3 2015 Acura L3 3
4 2016 BMW X5 2
5 2012 BMW X3 2
6 2013 BMW X4 2
7 2015 Acura L1 3
8 2011 Acura L2 3
9 2011 Audi L5 1
10 2012 Audi L6 1
Brands
Model_ID Title
---------------------
1 Audi
2 BMW
3 Acura
And following query:
SELECT
b.name,
i.title,
FROM
items AS i
INNER JOIN brands AS b
ON b.Model_ID = i.Model_ID
WHERE i.status = 1
ORDER BY i.created DESC;
The above produces working array:
Array
(
[0] => stdClass Object
(
[name] => 2010 Audi L1
[title] => Audi
)
[1] => stdClass Object
(
[name] => 2014 MBW X5
[title] => BMW
)
...
)
Than I use custom function to loop through array and end up with
Array
(
[Acura] => Array
(
[0] => stdClass Object
(
[name] => 2015 Acura L1
[title] => Acura
)
...
)
[BWM] => Array
(
[0] => stdClass Object
(
[name] => 2016 BMW X5
[title] => BWM
)
...
)
[Audi] => Array
(
[0] => stdClass Object
(
[name] => 2010 Audi L1
[title] => Audi
)
...
)
)
Now I can use foreach loop and limit each Brand to show x number of items, but the idea is to do it within database, so instead of pulling all records, I would like to be able to limit to 5 items per each brand.
Note: I did not list the rest of the table fields, such as created, which is used to sort records.
get brands:
$q = 'SELECT Model_ID AS id FROM brands ORDER BY title ASC';
$r = mysql_query($q);
$model_ids = array();
while($brand = mysql_fetch_assoc($r)) { $model_ids[] = $brand['id']; }
then make generate select query like this using Model_ID-s collected from 1st step or somewhere else (ex: from search form):
$q = array();
foreach($mode_ids AS $model_id) {
$q[] = '(SELECT b.name, i.title FROM items AS i INNER JOIN brands AS b ON b.Model_ID = i.Model_ID WHERE b.Model_ID = '.(int)$model_id.' AND i.status = 1 ORDER BY i.created DESC LIMIT 5)';
}
if(!empty($q)) {
$q = implode(' UNION ALL ', $q);
$r = mysql_query($q);
while($record = mysql_fetch_object($r)) {
var_dump($record);
}
}
as result we'll get records of resulting query:
(SELECT
b.name,
i.title FROM
items AS i
INNER JOIN brands AS b
ON b.Model_ID = i.Model_ID WHERE b.Model_ID=1 AND i.status = 1 ORDER BY i.created DESC LIMIT 5)
UNION ALL
(SELECT
b.name,
i.title FROM
items AS i
INNER JOIN brands AS b
ON b.Model_ID = i.Model_ID WHERE b.Model_ID=2 AND i.status = 1 ORDER BY i.created DESC LIMIT 5)
UNION ALL
(SELECT
b.name,
i.title FROM
items AS i
INNER JOIN brands AS b
ON b.Model_ID = i.Model_ID WHERE b.Model_ID=3 AND i.status = 1 ORDER BY i.created DESC LIMIT 5)

How can I merge the dates on this ledger?

I have tow tables for reciepts and expenture, am using union to merge them AND generate reports
SELECT MIN(date) AS trx, MAX(date), COUNT(*), SUM(amount), CONCAT(' - ','-'),
$f as _
FROM expenditure WHERE date >= '$start_date' AND date <= '$end_date'
AND client_id like '{$_SESSION['client']['id']}'
GROUP BY _
UNION
SELECT MIN(date) AS trx, MAX(date), COUNT(*),CONCAT(' - ','-'), SUM(amount),
$f as _
FROM receipts
WHERE date >= '$start_date' AND date <= '$end_date'
AND client_id like '{$_SESSION['client']['id']}'
GROUP BY _
ORDER BY trx
I get the following results
Array (
[0] => Array ( [trx] => 2012-03-06 [MAX(date)] => 2012-03-06 [COUNT(*)] => 1 [SUM(amount)] => 120000 [CONCAT(' - ','-')] => - - [_] => 2012-03-06 )
[1] => Array ( [trx] => 2012-03-08 [MAX(date)] => 2012-03-08 [COUNT(*)] => 1 [SUM(amount)] => 120000 [CONCAT(' - ','-')] => - - [_] => 2012-03-08 )
[2] => Array ( [trx] => 2012-06-06 [MAX(date)] => 2012-06-06 [COUNT(*)] => 2 [SUM(amount)] => 60000 [CONCAT(' - ','-')] => - - [_] => 2012-06-06 )
[3] => Array ( [trx] => 2012-06-06 [MAX(date)] => 2012-06-06 [COUNT(*)] => 1 [SUM(amount)] => - - [CONCAT(' - ','-')] => 487200 [_] => 2012-06-06 )
[4] => Array ( [trx] => 2012-06-08 [MAX(date)] => 2012-06-08 [COUNT(*)] => 1 [SUM(amount)] => 120000 [CONCAT(' - ','-')] => - - [_] => 2012-06-08 )
[5] => Array ( [trx] => 2012-06-29 [MAX(date)] => 2012-06-29 [COUNT(*)] => 2 [SUM(amount)] => 320000 [CONCAT(' - ','-')] => - - [_] => 2012-06-29 ) )
problem
If you notice on 3 and four there are two records for one day.
[trx] => 2012-06-06
Because on that day the client carried out both a credit and debit. is there way to just only combine rows like 3 and 4 into one row. SELECT DISTINCT dindnt work either or is it posible for mysql to sum records from both tables that occur on the same date.
You can use the subselect for your custom aliases to group
$query = "
SELECT q.*
FROM (
SELECT
MIN(date) AS trx,
MAX(date) as max_date,
COUNT(*),
SUM(amount),
CONCAT(' - ','-'),
$f as _
FROM
expenditure
WHERE
date >= '$start_date'
AND date <= '$end_date'
AND client_id like '{$_SESSION['client']['id']}'
GROUP BY _
UNION
SELECT
MIN(date) AS trx,
MAX(date),
COUNT(*),
CONCAT(' - ','-'),
SUM(amount),
$f as _
FROM
receipts
WHERE
date >= '$start_date'
AND date <= '$end_date'
AND client_id like '{$_SESSION['client']['id']}'
GROUP BY _ ORDER BY trx
) q GROUP BY q.max_date
";
Above query will group the results by MAX(date) as max_date
And for the sum of amount on same date you can modify as
$query = "
SELECT
q.*,
SUM(q.amount) AS grand_sum
FROM (
SELECT
MIN(date) AS trx,
MAX(date) as max_date,
COUNT(*),
SUM(amount) AS amount,
CONCAT(' - ','-'),
$f as _
FROM
expenditure
WHERE
date >= '$start_date'
AND date <= '$end_date'
AND client_id like '{$_SESSION['client']['id']}'
GROUP BY _
UNION
SELECT
MIN(date) AS trx,
MAX(date),
COUNT(*),
CONCAT(' - ','-'),
SUM(amount),
$f as _
FROM
receipts
WHERE
date >= '$start_date'
AND date <= '$end_date'
AND client_id like '{$_SESSION['client']['id']}'
GROUP BY _ ORDER BY trx
) q GROUP BY q.max_date
";
This grand_sum column has the sum of amount on the same date
You can UNION the expenditures and receipts tables first, then select from the unioned result. Note that the unioned result has separate columns for receipt.value and expenditure.value.
SELECT
MIN(date) as trx,
MAX(date),
COUNT(exp_amount) + COUNT(rct_amount) AS Tran_Count,
COALESCE(SUM(exp_amount), ' - -') AS Exp_Total,
COALESCE(SUM(rct_amount), ' - -') AS Rct_Total,
_
FROM (
SELECT whatever AS _, date, amount AS exp_amount, NULL AS rct_amount
FROM expenditure
WHERE date >= start AND date <= end AND client_id LIKE 'whatever'
UNION SELECT whatever, date, NULL, amount
FROM receipts
WHERE date >= start AND date <= end AND client_id LIKE 'whatever'
) exp_rct
GROUP BY _
ORDER BY trx

CodeIgniter SQL query - how to sum values for each month?

I have the following table:
//table_1
record_id user_id plant_id date cost
1 1 1 2011-03-01 10
2 1 1 2011-03-02 10
3 1 1 2011-04-10 5
4 1 2 2011-04-15 5
I would like to build a query (if possible using CI Active Records, but MySQL is fine) in which I generate the following result:
[1] => [1] => [March 2011] [20]
=> [April 2011] [5]
[2] => [March 2011] [0]
=> [April 2011] [5]
I have tried using $this->db->group_by but I think I'm not using it correctly.
If anyone could give me a pointer or roadmap to get this done it would be much appreciated -- thanks!
Sample table
drop table if exists t;
create table t( record_id int, user_id int, plant_id int, date datetime, cost float);
insert t select
1 ,1, 1 ,'2011-03-01', 10 union all select
2 ,1, 1 ,'2011-03-02', 10 union all select
3 ,1, 1 ,'2011-04-10', 5 union all select
4 ,1, 2 ,'2011-04-15', 5;
Because you want to see the row with 0, you need to do a cross join between the year-month and all user-plants.
select up.user_id, up.plant_id, ym2, ifnull(sum(t.cost),0) totalcost
from (select distinct date_format(date, '%Y-%m') ym, date_format(date, '%M %Y') ym2 from t) dates
cross join (select distinct t.user_id, t.plant_id from t) up
left join t on date_format(t.date, '%Y-%m') = dates.ym
and up.user_id=t.user_id
and up.plant_id=t.plant_id
group by up.user_id, up.plant_id, ym2, ym
order by up.user_id, up.plant_id, date(concat(ym,'-1'));
The fact that Month Year does not sort correctly also requires the complex treatment of the dates for ordering purposes at the end.
This is a pretty intense way to do it and it would be far preferable to store the month-year as a column itself if you need to make these queries frequently, but this is basically how it works:
SELECT CONCAT(MONTHNAME(date), ' ', YEAR(date)) AS monthyear, COUNT(*) AS count GROUP BY YEAR(date), MONTH(date), plant_id;
That should get you the resultset you're looking for.

sql sort numeric then alphabetically

in this example :
10-20
20-40
50-60
v
k
r
a
12 month
1 month
how can i sort it in this order ?:
10-20
20-40
50-60
a
k
r
v
1 month
12 month
i use abs(value) but in the alphabetical case doesn't work
If you can get away with doing some processing in PHP, you could use natsort:
Standard sorting
Array
(
[3] => img1.png
[1] => img10.png
[0] => img12.png
[2] => img2.png
)
Natural order sorting
Array
(
[3] => img1.png
[2] => img2.png
[1] => img10.png
[0] => img12.png
)
Otherwise, there's another question on SO which asks the same thing: Natural Sort in MySQL
OK, thanks to the commenter, now a working version. This sorts on two cases in the order by clause:
select *
from (
select '10-20' as col1
union all select '20-40'
union all select '50-60'
union all select 'v'
union all select 'k'
union all select 'r'
union all select 'a'
union all select '12 month'
union all select '1 month'
) s1
order by
case
when col1 rlike '[0-9][0-9]-[0-9][0-9]' then 1
when col1 rlike '[0-9]+ month' then 3
else 2
end
, case
when col1 rlike '[0-9][0-9]-[0-9][0-9]' then cast(col1 as decimal)
when col1 rlike '[0-9]+ month' then cast(col1 as decimal)
else col1
end
The first case puts categories in order: 00-00 first, then other stuff, and at the end the months. The second case converts the columns to decimal if possible.

Categories