How can I merge the dates on this ledger? - php

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

Related

Group concat data values by year

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;

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;

Order by not working with group by clause

I am using corequery of mysql in cakephp. I want the records in descending order. This is my table structure
enter code here
$coreQueryUser = $this->Message->query(
"select * from messages where messages.list_id = 3
group By (if(sender_id > reciever_id, sender_id, reciever_id)),
(if(sender_id > reciever_id, reciever_id, sender_id))
order by id desc
"
);
I want last message that belongs to (sender_id and reciver_id and viceversa) that belongs to list id 3
when i run this query i get the following output
<pre>Array
(
[0] => Array
(
[messages] => Array
(
[id] => 1
[sender_id] => 21
[reciever_id] => 10
[list_id] => 3
[message] => hello
[add_date] => 2016-09-25 00:00:00
[is_check] => 0
)
)
[1] => Array
(
[messages] => Array
(
[id] => 3
[sender_id] => 22
[reciever_id] => 10
[list_id] => 3
[message] => hello s
[add_date] => 2016-09-25 16:39:41
[is_check] => 0
)
)
)
but i wnat result like that:
Array
(
[0] => Array
(
[messages] => Array
(
[id] => 2
[sender_id] => 10
[reciever_id] => 21
[list_id] => 3
[message] => hello sir
[add_date] => 2016-09-25 00:00:00
[is_check] => 0
)
)
[1] => Array
(
[messages] => Array
(
[id] => 6
[sender_id] => 22
[reciever_id] => 10
[list_id] => 3
[message] => new
[add_date] => 2016-09-25 16:39:41
[is_check] => 0
)
)
)
Can anyone help me :(
The problem is that your query is against the sql standard because you have several fields in the select list that are neither in the group by list, nor are subject of an aggregate function, such as sum(). MySQL unfortunately allows such invalid queries to run under certain sql mode settings (the default settings of the most recent versions of MySQL would prevent such queries from running).
As MySQL documentation on group by clause says (bolding is mine):
If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard
SQL use of GROUP BY permits the select list, HAVING condition, or
ORDER BY list to refer to nonaggregated columns even if the columns
are not functionally dependent on GROUP BY columns. This causes MySQL
to accept the preceding query. In this case, the server is free to
choose any value from each group, so unless they are the same, the
values chosen are indeterminate, which is probably not what you want.
Furthermore, the selection of values from each group cannot be
influenced by adding an ORDER BY clause. Result set sorting occurs
after values have been chosen, and ORDER BY does not affect which
value within each group the server chooses.
You apparently want the latest record (with max(id) for each group. The proper way is to have a subquery that returns the max(id) per group and in the outer query join back to your main table using the ids to get the value of the other fields:
select m.*
from
messages m
inner join (
select max(id) as maxid
from messages
where messages.list_id = 3
group By (if(sender_id > reciever_id, sender_id, reciever_id)),
(if(sender_id > reciever_id, reciever_id, sender_id))
) t1 on m.id=t1.maxid
This code working:
SELECT * FROM generate_invoice
WHERE id IN
(
SELECT max(id) as id
FROM generate_invoice
GROUP by pay_id
ORDER by id DESC
)
How to group by DESC order
Try
SELECT * FROM ( SELECT * FROM generate_invoice ORDER BY id DESC ) AS g GROUP BY g.pay_id
OR
Use this code
SELECT m1.*,m2.* FROM generate_invoice m1 LEFT JOIN generate_invoice m2 ON (m1.pay_id = m2.pay_id AND m1.id < m2.id ) order by m1.id desc

UNION in MYSQL and merge columns

I am fetching data from two tables in mysql. I have query like this:
SELECT SUM(IF(debit IS NULL, 0, debit)) - SUM(IF(credit IS NULL, 0, credit)) as amount, th.country_id, cl.currency_name
FROM account_treasury_hq th
LEFT JOIN system_country_list cl ON cl.country_id=th.country_id
WHERE th.company_id='$company_id'
GROUP BY th.country_id
UNION
SELECT SUM(CASE WHEN ce_type = 'IN' or ce_type is NULL then payment_amount
WHEN ce_type = 'OUT' then - payment_amount
END) as amount,
source_country_id as country_id, cl.currency_name
FROM customer_payment_options cpo
LEFT JOIN system_country_list cl ON cl.country_id=cpo.source_country_id
WHERE cpo.company_id='$company_id'
GROUP BY cpo.source_country_id
Result
Array
(
[0] => Array
(
[amount] => -345
[country_id] => 40
[currency_name] => Canadian dollar
)
[1] => Array
(
[amount] => 93
[country_id] => 210
[currency_name] => Sri Lankan rupee
)
[2] => Array
(
[amount] => 46.5
[country_id] => 236
[currency_name] => United States dollar
)
[3] => Array
(
[amount] => 916249.8999999999
[country_id] => 40
[currency_name] => Canadian dollar
)
[4] => Array
(
[amount] => -6670.1
[country_id] => 210
[currency_name] => Sri Lankan rupee
)
[5] => Array
(
[amount] => 2017.67
[country_id] => 236
[currency_name] => United States dollar
)
)
In the result array, you can see the same country_id. I want to merge those same country id with 1. It means, there should only be each amount information for each country_id.
(amount should be added by adding multiple countries)
Is there any way to do it in PHP or mysql itself?
Thanks for any kind of help.
Required Result
Array
(
[0] => Array
(
[amount] => 915904.89
[country_id] => 40
[currency_name] => Canadian dollar
)
[1] => Array
(
[amount] => -6577.1
[country_id] => 210
[currency_name] => Sri Lankan rupee
)
[2] => Array
(
[amount] => 2064.17
[country_id] => 236
[currency_name] => United States dollar
)
)
just select it all in an outer query sum the amount and group by country_id
SELECT
SUM(amount) country_id, currency_name
FROM
( SELECT
SUM(IF(debit IS NULL, 0, debit)) - SUM(IF(credit IS NULL, 0, credit)) as amount,
th.country_id,
cl.currency_name
FROM account_treasury_hq th
LEFT JOIN system_country_list cl ON cl.country_id=th.country_id
WHERE th.company_id='$company_id'
GROUP BY th.country_id
UNION
SELECT
SUM(CASE
WHEN ce_type = 'IN' or ce_type is NULL then payment_amount
WHEN ce_type = 'OUT' then - payment_amount
END
) as amount,
source_country_id as country_id,
cl.currency_name
FROM customer_payment_options cpo
LEFT JOIN system_country_list cl ON cl.country_id=cpo.source_country_id
WHERE cpo.company_id='$company_id'
GROUP BY cpo.source_country_id
) t
GROUP BY country_id
You're doing a union, with grouping in the individual subquery. Each subquery in a union has NO awareness of what the other parallel queries are doing. Each query's group by will apply only to that ONE query.
You'd need to do the heavy duty work in a wrapper query, e.g.
SELECT SUM(.....)
FROM (
SELECT th.country_id AS country_id, etc...
UNION ALL
SELECT source_country AS country_id, etc...
) AS foo
GROUP BY country_id
e.g. do all the grouping and "math" in the outer query, and the inner queries simply retrieve all the records you do want grouped together.
Didn't test this (would need a sqlfiddle to play with) but you'd want to aggregate them in the query. Something like this (you'll want to tweak the group by and select for your needs):
SELECT SUM( x.amount ), x.country_id, x.currency_name
FROM
(
SELECT SUM(IF(debit IS NULL, 0, debit)) - SUM(IF(credit IS NULL, 0, credit)) as amount, th.country_id, cl.currency_name
FROM account_treasury_hq th
LEFT JOIN system_country_list cl ON cl.country_id=th.country_id
WHERE th.company_id='$company_id'
GROUP BY th.country_id
UNION
SELECT SUM(CASE WHEN ce_type = 'IN' or ce_type is NULL then payment_amount
WHEN ce_type = 'OUT' then - payment_amount
END) as amount,
source_country_id as country_id, cl.currency_name
FROM customer_payment_options cpo
LEFT JOIN system_country_list cl ON cl.country_id=cpo.source_country_id
WHERE cpo.company_id='$company_id'
GROUP BY cpo.source_country_id
) AS x
GROUP BY x.amount, x.country_id, x.currency_name

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