union of two queries to get the result in mysql - mysql

I'm confused about this:
SELECT request_id, SUM( cc_amount ) AS amt,
DATE_FORMAT( transaction_datetime, '%b %y' ) AS tdate
FROM ee_request_cc
GROUP BY DATE_FORMAT( transaction_datetime, '%b %y' )
UNION
SELECT request_id, SUM( request_amount ) AS amt,
DATE_FORMAT( transaction_date, '%b %y' ) AS tdate
FROM ee_request_paypal
GROUP BY DATE_FORMAT( transaction_date, '%b %y' )
I'm getting:
id amt tdate
20 86.00 Mar 12
80 5.00 Apr 12
23 55.00 Mar 12
Whereas I want to add all amounts for March and April like:
id amt tdate
20 141.00 Mar 12
80 5.00 Apr 12
Please suggest me the change in my query.

Try this:
select sum(amt), DATE_FORMAT(tdate, '%b %y') from (
SELECT amount1 AS amt, transaction_dt AS tdate
FROM table1
UNION ALL
SELECT request_amount, tr_date
FROM table2
) s
GROUP BY YEAR(tdate), MONTH(tdate)
Note you shouldn't take an ID from a grouped result... which one would you take?
Also grouping by numbers must be faster than transforming a date to a string and then grouping by string.

You need to get all the results, and then perform the aggregation:
SELECT table_both.request_id, table_both.tdate, SUM(table_both.amount) AS amt
FROM (SELECT request_id, cc_amount AS amt, DATE_FORMAT(transaction_datetime,'%b %y') AS tdate
FROM table1
UNION ALL --Don't remove the duplicates
SELECT request_id, request_amount, DATE_FORMAT(transaction_date,'%b %y')
FROM table2) table_both
GROUP BY table_both.request_id, table_both.tdate --You don't need to add the format function in the group by

You use an englobing SELECT with sum on amt and GROUP BY tdate
SELECT id, SUM(amt) as samt, tdate
FROM (YOUR_ABOBE_QUERY) AS thequery
GROUP BY thequery.tdate

Related

Dividing two COUNTS from mysql

Friends, I am trying to divide two COUNT(*) from MySQL:
I have this query:
SELECT 'Total ', COUNT(*)
FROM root4
WHERE str_to_date(DATE, '%d.%m.%Y') = CURDATE()
UNION
SELECT 'Good', COUNT(*)
FROM root4
WHERE str_to_date(DATE, '%d.%m.%Y') = CURDATE()
AND testresult ='OK'
The output of this query is looking like this:
________________________
|Total | COUNT(*) |
________________________
|Total| 42 |
|Good | 34 |
_______________________
What I want to achieve is to make another row under "Good" called "FPY" but the value to the dividing of "Good" to "Total" in percentage.
Something like this:
________________________
|Total | COUNT(*) |
________________________
|Total| 42 |
|Good | 34 |
|FPY | 80.95 |
_______________________
I tried to divide them like noob:
SELECT 'Total ', COUNT(*)
FROM root4
WHERE str_to_date(DATE, '%d.%m.%Y') = CURDATE()
UNION
SELECT 'Good', COUNT(*)
FROM root4 WHERE str_to_date(DATE, '%d.%m.%Y') = CURDATE()
AND testresult ='OK'
UNION
SELECT 'FPY', (COUNT(*)
FROM root4
WHERE str_to_date(DATE, '%d.%m.%Y') = CURDATE() /
UNION
SELECT 'Good', COUNT(*)
FROM root4
WHERE str_to_date(DATE, '%d.%m.%Y') = CURDATE()
AND testresult ='OK')
Of course, this is not working...
Note: Colum DATE is varchar that`s why I am using str_to_date.
Look for this:
SELECT COUNT(*) AS Total,
SUM(testresult ='OK') AS Good,
100 * COUNT(*) / SUM(testresult ='OK') AS FPY
FROM root4
WHERE `date` = DATE_FORMAT(CURRENT_DATE, '%d.%m.%Y')
is there any way to print it in two columns as I post in the question? – Azim Feta
WITH cte AS (
SELECT COUNT(*) AS Total,
SUM(testresult ='OK') AS Good
FROM root4
WHERE `date` = DATE_FORMAT(CURRENT_DATE, '%d.%m.%Y')
)
SELECT 'Total' AS indicator, Total AS value FROM cte
UNION ALL
SELECT 'Good', Good FROM cte
UNION ALL
SELECT 'FPY', 100 * Good / Total FROM cte
I think you could be needing a "SubQuery" here.
Something like this:
SELECT
Count(root4.*) AS Total,
root4_1.Good AS Good,
COUNT(root4.*) / root4_1.Good AS FYP
FROM
root4,
(
SELECT
COUNT(*) AS Good
FROM
root4
WHERE
str_to_date(DATE, '%d.%m.%Y') = CURDATE()
AND
testresult ='OK'
)AS root4_1
WHERE
str_to_date(DATE, '%d.%m.%Y') = CURDATE()
Also, see this question, which is similar: How to SELECT based on value of another SELECT

Finding the difference between consecutive rows in MySQL without LAG?

For each month (except January) I would like to find the difference in quantity from the previous month.
I have a table:
Month
Qty
January
4
February
3
March
9
April
3
May
7
and I would like to return:
Month
Difference
February
-1
March
6
April
-6
May
4
I'm using an older version of MySQL, so I can't use LEAD/LAG for this.
SELECT t1.`Month`, COALESCE(t1.Qty - t2.Qty, 'Unknown') Difference
FROM table t1
LEFT JOIN table t2
ON STR_TO_DATE(CONCAT(t1.`Month`, ' 01 2021'), '%M %d %Y')
= STR_TO_DATE(CONCAT(t2.`Month`, ' 01 2021'), '%M %d %Y') + INTERVAL 1 MONTH
You can use a correlated subquery to get the previous value. If the first column is really ordered, then you can use:
select t.*, (qty - prev_qty) as difference
from (select t.*,
(select t2.qty
from t t2
where t2.month < t.month
order by t2.month desc
limit 1
) as prev_qty
from t
) t
where prev_qty is not null;
If you are really storing months as a string name, then you need to convert to something orderable:
select t.*, (qty - prev_qty) as difference
from (select t.*,
(select t2.qty
from t t2
where str_to_date(concat(t2.month, ' 1 2000', '%M %d %Y') < str_to_date(concat(t.month, ' 1 2000', '%M %d %Y')
order by str_to_date(concat(t2.month, ' 1 2000', '%M %d %Y') desc
limit 1
) as prev_qty
from t
) t
where prev_qty is not null;

Calculate index position using sub queries on group by using date

select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
sponsor_id sum(points) created_at
1 30 2014-12-08 10:54:59
2 25 2014-12-09 05:43:11
3 20 2014-12-09 06:58:40
1 5 2014-12-09 05:56:12
1 34 2014-08-23 10:42:32
here I want to calculate rank of particular sponsor using sponsor_id on daily basis .. I want to build a query that can return me something like as displayed below:
sponsor_id rank created_at
1 1 2014-12-08 10:54:59
1 3 2014-12-09 05:56:12
1 1 2014-08-23 10:42:32
I think I can use sub query like
select *
from (select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
) as t
where t.sponsor_id = 1
but how to calulate rank here.
Try this:
SELECT sponsor_id, points, created_at,
IF(#dte=#dte:=DATE(created_at), #rank:=#rank+1, #rank:=1) AS rank
FROM (SELECT user_id AS sponsor_id, SUM(points) points, created_at
FROM points_history
WHERE created_at BETWEEN '2014-08-12' AND '2015-08-12' AND
transaction_type = "debit"
GROUP BY user_id, DATE_FORMAT(created_at,"%d %M %Y")
ORDER BY DATE(created_at), SUM(points) DESC
) AS A, (SELECT #rank:=0, #dte:='') AS B
ORDER BY DATE(created_at), points DESC;

Mysql to select month-wise record even if data not exist

I wrote a query to get month-wise record in user table as follows
SELECT COUNT( `userID` ) AS total, DATE_FORMAT( `userRegistredDate` , '%b' ) AS
MONTH , YEAR( `userRegistredDate` ) AS year
FROM `users`
GROUP BY DATE_FORMAT( FROM_UNIXTIME( `userRegistredDate` , '%b' ) )
Output:
total MONTH year
---------------------------
3 May 2013
2 Jul 2013
--------------------------
Expected Output:
total MONTH year
---------------------------
0 Jan 2013
0 Feb 2013
0 Mar 2013
0 Apr 2013
3 May 2013
0 Jun 2013
2 Jul 2013
--------------------------
I need to show the record even if data not exist. How to do this?
I won't say much about efficiency as I have not tested it against other methods but without having a temp table this seem a fair way to go.
SELECT COUNT(u.userID) AS total, m.month
FROM (
SELECT 'Jan' AS MONTH
UNION SELECT 'Feb' AS MONTH
UNION SELECT 'Mar' AS MONTH
UNION SELECT 'Apr' AS MONTH
UNION SELECT 'May' AS MONTH
UNION SELECT 'Jun' AS MONTH
UNION SELECT 'Jul' AS MONTH
UNION SELECT 'Aug' AS MONTH
UNION SELECT 'Sep' AS MONTH
UNION SELECT 'Oct' AS MONTH
UNION SELECT 'Nov' AS MONTH
UNION SELECT 'Dec' AS MONTH
) AS m
LEFT JOIN users u
ON MONTH(STR_TO_DATE(CONCAT(m.month, ' 2013'),'%M %Y')) = MONTH(u.userRegistredDate)
AND YEAR(u.userRegistredDate) = '2013'
GROUP BY m.month
ORDER BY 1+1;
If you make the union based on a date format you can even reduce the work and load on the query.
SELECT COUNT(u.userID) AS total, DATE_FORMAT(merge_date,'%b') AS month, YEAR(m.merge_date) AS year
FROM (
SELECT '2013-01-01' AS merge_date
UNION SELECT '2013-02-01' AS merge_date
UNION SELECT '2013-03-01' AS merge_date
UNION SELECT '2013-04-01' AS merge_date
UNION SELECT '2013-05-01' AS merge_date
UNION SELECT '2013-06-01' AS merge_date
UNION SELECT '2013-07-01' AS merge_date
UNION SELECT '2013-08-01' AS merge_date
UNION SELECT '2013-09-01' AS merge_date
UNION SELECT '2013-10-01' AS merge_date
UNION SELECT '2013-11-01' AS merge_date
UNION SELECT '2013-12-01' AS merge_date
) AS m
LEFT JOIN users u
ON MONTH(m.merge_date) = MONTH(u.userRegistredDate)
AND YEAR(m.merge_date) = YEAR(u.userRegistredDate)
GROUP BY m.merge_date
ORDER BY 1+1;
Live DEMO of both queries.
You may need a table to hold every "month" record. A temp table can do the trick:
drop table if extists temp_months;
create temporary table temp_months
month date,
index idx_date(month);
insert into temp_months
values ('2013-01-31'), ('2013-02-28'), ...
And now, you can left join your data with this newly created temp table:
SELECT
COUNT( `userID` ) AS total,
DATE_FORMAT( m.month , '%b' ) AS
MONTH ,
YEAR( m.month ) AS year
FROM
months as m
left join `users` as u on m.month = last_day(FROM_UNIXTIME(`userRegistredDate`, '%b' )
GROUP BY
last_day(m.month);
Notice that you can put the temp table creation (and fill) in a stored procedure.
I use last_day for simplicity, but you are free to use any date in the month that you like, if you join it correctly.
Hope this helps

Issue with ORDER BY not sorting properly with sub selects

In the SQL statement below the results are returned out of order. If they were in ascending or descending order it would make sense, but it seems there is no order here.
Any insights as to why the ORDER BY function isn't sorting by the 'date' alias would be very much appreciated.
SQL Statement:
SELECT id, date, type
FROM (SELECT resume_id AS id, DATE_FORMAT( date_mod, '%M %e, %Y' ) AS date, 'resume' AS TYPE
FROM resumes
WHERE user_id = '$user_id'
UNION ALL
SELECT profile_id, DATE_FORMAT( date_mod, '%M %e, %Y' ) AS date, 'profile'
FROM profiles
WHERE user_id = '$user_id'
ORDER BY date DESC LIMIT 5) AS d1
ORDER BY date
Results:
Resume was updated on February 14, 2012
Resume was updated on February 15, 2012
Resume was updated on February 15, 2012
Resume was updated on February 9, 2012
Profile was updated on February 9, 2012
It's sorting them as strings because you've converted the dates to strings using DATE_FORMAT (note that as a string "February 15..." is lower than "February 9..." because 1 comes before 9 in the "alphabet"). The solution is to sort by the real date in date_mod. You could do it directly by just adding in date_mod to the selects and changing the order, like this:
SELECT id, date, date_mod, type
FROM (
SELECT resume_id AS id,
DATE_FORMAT(date_mod, '%M %e, %Y') AS date,
date_mod,
'resume' AS type
FROM resumes
WHERE user_id = '$user_id'
UNION ALL
SELECT profile_id AS id,
DATE_FORMAT(date_mod, '%M %e, %Y') AS date,
date_mod,
'profile' AS type
FROM profiles
WHERE user_id = '$user_id'
ORDER BY date_mod DESC
LIMIT 5
) AS d1
ORDER BY date_mod
But even better would be to simplify it by selecting only date_mod in the sub-query (i.e. no formatted version) and doing the DATE_FORMAT last in the outer query:
SELECT id, DATE_FORMAT(date_mod, '%M %e, %Y') AS date, type
FROM (
SELECT resume_id AS id, date_mod, 'resume' AS type
FROM resumes
WHERE user_id = '$user_id'
UNION ALL
SELECT profile_id AS id, date_mod, 'profile' AS type
FROM profiles
WHERE user_id = '$user_id'
ORDER BY date_mod DESC
LIMIT 5
) AS d1
ORDER BY date_mod
It's ordering your formatted date strings as strings, and you want to order them as dates. I would order by date_mod directly, then convert the output in the SELECT clause.