Combining sql select queries - mysql

I have 2 different queries, which both return results similar to below. For example
SELECT value
, date_format( statdate, "%Y-%m-%d" ) AS testdate
FROM stats
WHERE name LIKE 'myname1'
AND date >= "2014-09-20"
and date < "2014-09-24"
GROUP BY 2;
SELECT value
, date_format( statdate, "%Y-%m-%d" ) AS testdate
FROM stats
WHERE name LIKE 'myname2'
AND date >= "2014-09-20"
and date < "2014-09-24"
GROUP BY 2;
Information comes from the same table, but part of it is a different WHERE (with the date being the field which is the same in both).
Which both return information like..
value| testdate |
+--------+-------+
| 60 | 2014-09-20|
| 57 | 2014-09-21|
| 56 | 2014-09-22|
| 55 | 2014-09-23|
| 59 | 2014-09-24|
What I would like to do, is combine both sets of results into one (and then perform some maths like a ratio on the two numbers), so I want to end up with something like...
val1 | val2| ratio (val1/val2) | testdate |
+----+-------------------------|------------+
| 60 | 140 | 0.42 | 2014-09-20 |
| 57 | 180 | 0.31 | 2014-09-21 |
| 56 | 190 | 0.29 | 2014-09-22 |
| 55 | 10 | 5.5 | 2014-09-23 |
| 59 | 100 | 0.59 | 2014-09-24 |
I'm guessing I need a JOIN somewhere, but can't figure it, and how to use that value for some basic maths in there ?

You should use the "Self Join" to join the 2 instances of same table. like below
SELECT s1.value, date_format( s1.statdate, "%Y-%m-%d" ) AS testdate
s2.value, date_format( s2.statdate, "%Y-%m-%d" ) AS testdate2,
(s1.value/s2.value) as ration
FROM stats s1, stats s2
WHERE s1.id = s2.id
and s1.name LIKE 'myname1' AND s1.date >= "2014-09-20" and s1.date < "2014-09-24"
and s2.name LIKE 'myname2' AND s2.date >= "2014-09-20" and s2.date < "2014-09-24"
GROUP BY 2;

I would recommend conditional aggregation:
SELECT date_format(statdate, '%Y-%m-%d') AS testdate,
max(case when name = 'myname1' then value end) as val1,
max(case when name = 'myname2' then value end) as val2,
(max(case when name = 'myname1' then value end) /
max(case when name = 'myname2' then value end)
) as ratio,
FROM stats
WHERE name in ('myname1', 'myname2') AND date >= '2014-09-20' and date < '2014-09-24'
GROUP BY date_format(statdate, '%Y-%m-%d');

Related

Merge two queries that use different timeframes for min/max/avg

I am using 10.1.39-MariaDB - mariadb.org binary and I have the following table:
| id | date | ticker | close |
|-------|---------------------|--------|-----------|
| 39869 | 2019-09-18 00:00:00 | AAPL | 221.96160 |
| 39870 | 2019-09-17 00:00:00 | AAPL | 220.70000 |
| 39871 | 2019-09-16 00:00:00 | AAPL | 219.90000 |
| 39872 | 2019-09-13 00:00:00 | AAPL | 218.75000 |
| 39873 | 2019-09-12 00:00:00 | AAPL | 223.09000 |
| 39874 | 2019-09-11 00:00:00 | AAPL | 223.59000 |
| 39875 | 2019-09-10 00:00:00 | AAPL | 216.70000 |
I have two queries where I am calculating metrics for 52-weeks and the second query calculates aggregation metrics for 20-days:
52-weeks:
SELECT
Y.*
FROM
(
SELECT
MAX(CLOSE) AS week_52_High,
DATE AS week_52_High_date,
MIN(CLOSE) AS week_52_Low,
DATE AS week_52_Low_date,
AVG(CLOSE) AS week_52_Avg
FROM
`prices`
WHERE
DATE >= DATE(NOW()) - INTERVAL 52 WEEK AND ticker = "AAPL") Y
LEFT JOIN prices tmax52 ON
tmax52.date = Y.week_52_High_date AND tmax52.close = week_52_High
LEFT JOIN prices tmin52 ON
tmin52.date = Y.week_52_Low_date AND tmin52.close = week_52_Low
LEFT JOIN prices tavg52 ON
tavg52.close = week_52_Avg
20-days
SELECT
Y.*
FROM
(
SELECT
MAX(CLOSE) AS day_20_High,
DATE AS day_20_High_date,
MIN(CLOSE) AS day_20_Low,
DATE AS day_20_Low_date,
AVG(CLOSE) AS day_20_Avg
FROM
`prices`
WHERE
DATE >= DATE(NOW()) - INTERVAL 20 DAY AND ticker = "AAPL") Y
LEFT JOIN prices tmax20 ON
tmax20.date = Y.day_20_High_date AND tmax20.close = day_20_High
LEFT JOIN prices tmin20 ON
tmin20.date = Y.day_20_Low_date AND tmin20.close = day_20_Low
LEFT JOIN prices tavg20 ON
tavg20.close = day_20_Avg
Both queries calculate the min/max/avg for each close price and attach the days, when this happened.
Any suggestions how to merge these two queries to get everything in 1 output?
I appreciate your replies!
Your first problem is that your query won't actually work. The correct way to get the dates for the high and low values is as below. Note that it is meaningless to try and get a date for the average close as it's highly unlikely that the stock will have closed at that price.
SELECT
Y.day_20_High,
tmax20.DATE AS day_20_High_date,
Y.day_20_Low,
tmin20.DATE AS day_20_Low_date,
Y.day_20_Avg
FROM
(
SELECT
MAX(CLOSE) AS day_20_High,
MIN(CLOSE) AS day_20_Low,
ROUND(AVG(CLOSE),2) AS day_20_Avg
FROM
`prices`
WHERE
DATE >= CURDATE() - INTERVAL 20 DAY AND ticker = "AAPL") Y
LEFT JOIN prices tmax20 ON tmax20.close = Y.day_20_High
LEFT JOIN prices tmin20 ON tmin20.close = Y.day_20_Low
Output (for my sample data)
day_20_High day_20_High_date day_20_Low day_20_Low_date day_20_Avg
107.50 2019-09-20 101.10 2019-09-10 104.05
Demo on dbfiddle
Having corrected the query, you can now just JOIN to the same query for 52-week data:
SELECT
Y20.day_20_High,
tmax20.DATE AS day_20_High_date,
Y20.day_20_Low,
tmin20.DATE AS day_20_Low_date,
Y20.day_20_Avg,
Y52.week_52_High,
tmax52.DATE AS week_52_High_date,
Y52.week_52_Low,
tmin52.DATE AS week_52_Low_date,
Y52.week_52_Avg
FROM ((
SELECT
MAX(CLOSE) AS day_20_High,
MIN(CLOSE) AS day_20_Low,
ROUND(AVG(CLOSE),2) AS day_20_Avg
FROM
`prices`
WHERE
DATE >= CURDATE() - INTERVAL 20 DAY AND ticker = "AAPL") Y20
LEFT JOIN prices tmax20 ON tmax20.close = Y20.day_20_High
LEFT JOIN prices tmin20 ON tmin20.close = Y20.day_20_Low)
JOIN ((
SELECT
MAX(CLOSE) AS week_52_High,
MIN(CLOSE) AS week_52_Low,
ROUND(AVG(CLOSE),2) AS week_52_Avg
FROM
`prices`
WHERE
DATE >= DATE(NOW()) - INTERVAL 52 WEEK AND ticker = "AAPL") Y52
LEFT JOIN prices tmax52 ON tmax52.close = Y52.week_52_High
LEFT JOIN prices tmin52 ON tmin52.close = Y52.week_52_Low)
Output (for my sample data)
day_20_High day_20_High_date day_20_Low day_20_Low_date day_20_Avg week_52_High week_52_High_date week_52_Low week_52_Low_date week_52_Avg
107.50 2019-09-20 101.10 2019-09-10 104.05 109.70 2019-08-24 100.00 2019-08-21 104.19
Demo on dbfiddle
If you want to merge your queries one after another:
with t as (
select 0 as id, 1 as v0, 2 as v1
union all select 1 as id, 1 as v0, 2 as v1
)
select *
from (
select t.*, 'weekly_report' as typ from t
union all
select t.*, 'monthly_report' as typ from t
) as t1
id | v0 | v1 | typ
-: | -: | -: | :-------------
0 | 1 | 2 | weekly_report
1 | 1 | 2 | weekly_report
0 | 1 | 2 | monthly_report
1 | 1 | 2 | monthly_report
db<>fiddle here
And to have your results column by column, do a join:
with t as (
select 0 as id, 1 as v0, 2 as v1
union all select 1 as id, 1 as v0, 2 as v1
)
select t0.id, t0.v0, t0.v1, t1.v0, t1.v1
from (
select * from t
) as t0
full join (
select * from t
) as t1 on t0.id = t1.id
id | v0 | v1 | v0 | v1
-: | -: | -: | -: | -:
0 | 1 | 2 | 1 | 2
1 | 1 | 2 | 1 | 2
db<>fiddle here

How to remove decimal digit from result in mysql?

I want to remove digit after decimal how to solve it?
My query is:
SELECT city_name,
Assignedto,
COUNT(Assignedto) AS TC,
CONCAT(count(CASE
WHEN STATUS = 'CLOSED' THEN 1
ELSE NULL
END) * 100 / count(1), '%') AS SC,
CONCAT(count(CASE
WHEN STATUS = 'PENDING' THEN 1
ELSE NULL
END) * 100 / count(1), '%') AS PC,
SUM(TIMESTAMPDIFF(MINUTE,Request_Date, Xetr))/60 AS WH,
(154440-sum(TIMESTAMPDIFF(MINUTE,Request_Date, Xetr))/60) AS VH,
CONCAT(COUNT(Feedback_Rate)/COUNT(Assignedto)*100,'%') AS Feed_Percent,
SUM(Feedback_Rate)/(count(Feedback_Rate)*5)*5 AS AVG_Feedback
FROM `it_service_ticket`
INNER JOIN `it_problem`ON `it_service_ticket`.`it_problem_id`=`it_problem`.`it_problem_id`
INNER JOIN `city_master` ON `it_service_ticket`.cityid=`city_master`.city_id
WHERE `it_service_ticket`.`xetr` BETWEEN '2016-04-01 12:00:00 AM' AND '2017-02-28 12:00:00 PM'
GROUP BY Assignedto
ORDER BY city_name ASC;
Output
+-------------------------+-------------------------+-------+------------+----------+------------+--------------+-----------+---------+
| City_Name | AssigneeTo | TC | SC | PC | WH | VH | Feedback | Average |
+-------------------------+-------------------------+-------+------------+----------+------------+--------------+-----------+---------+
| Ahmedabad | mahesh.patel#corp.in | 297 | 100.0000% | 0.0000% | 147.0667 | 154292.9333 | 43.4343% | 4.4031 |
| Ahmedabad | mahesh.patel#corp.in | 297 | 100.0000% | 0.0000% | 147.0667 | 154292.9333 | 43.4343% | 4.4031 |
If you want to round off decimal places, use ROUND(yourColumn,0) function.
So 13.78 will become 14
If you want to get rid of decimal places, user FLOOR(yourColumn)
So 13.78 will become 13
So for example
SUM(TIMESTAMPDIFF(MINUTE,Request_Date, Xetr))/60 AS WH
should be changed to
ROUND(SUM(TIMESTAMPDIFF(MINUTE,Request_Date, Xetr))/60,0) AS WH
Edit: This would take care of your %.
CONCAT(
ROUND(count(CASE
WHEN STATUS = 'PENDING' THEN 1
ELSE NULL
END) * 100 / count(1),0)
, '%') AS PC
Do the same for all the columns you need to remove decimal places.
You Should Use TRUNCATE() in Mysql for Example
SELECT TRUNCATE(525.668545, 3) -- 525.668

MySQL UNION does not seem to work correctly

I have an SQL query I am using to pull data from an orders database. I am querying 2 tables and combining the results using UNION ALL. However, the UNION ALL does not seem to work as expected. Here is the query I am using:
SELECT year(oc_order.date_added) AS year, COUNT(oc_order.order_id) as cnt, SUM( ifnull(oc_order.new_total,oc_order.total) ) as total
FROM oc_order
WHERE oc_order.order_status_id IN (1,3,5)
AND MONTH(oc_order.date_added) BETWEEN '01' AND '02'
AND DAY(oc_order.date_added) BETWEEN '01' AND '31'
GROUP BY year(oc_order.date_added)
UNION ALL
SELECT ifnull(year(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),year(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) ) AS year, COUNT(oc_return_custom.return_id) as cnt, SUM( oc_return_custom.total ) as total
FROM oc_return_custom
WHERE ifnull(MONTH(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),MONTH(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) ) BETWEEN '01' AND '02'
AND ifnull(DAY(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),DAY(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) ) BETWEEN '01' AND '31'
GROUP BY ifnull(year(str_to_date(oc_return_custom.date_added,'%d-%m-%Y %H:%i:%s')),year(str_to_date(oc_return_custom.date_added,'%Y-%m-%d %H:%i:%s')) )
ORDER BY year DESC
This is what I get from the query:
+=======+========+=======+
| year | cnt | total |
+=======+========+=======+
| 2016 | 200 | 1000 |
| 2016 | 50 | 200 |
| 2015 | 100 | 800 |
| 2015 | 10 | 50 |
+=======+========+=======+
But this is what I wanted to get:
+=======+========+=======+
| year | cnt | total |
+=======+========+=======+
| 2016 | 250 | 1200 |
| 2015 | 110 | 850 |
+=======+========+=======+
Can someone tell me what I am doing wrong???
Notes:
The oc_order table's date_added column is datetime whereas oc_return_custom 's date_added column is just text.
UNION ALL simply puts together two data sets produced by separate GROUP BY operations.
To get the expected result set you have to wrap the query in a subquery and apply an additional GROUP BY:
SELECT year, SUM(cnt) AS cnt, SUM(total) AS total
FROM ( ... your query here ...) AS t
GROUP BY year

MySQL : How to convert multiple row to single row? in mysql

I want to convert multiple rows to a single row, based on week. It should look like the following. Can any one help me?
id | Weight | Created |
1 | 120 | 02-04-2012 |
2 | 110 | 09-04-2012 |
1 | 100 | 16-04-2012 |
1 | 130 | 23-04-2012 |
2 | 140 | 30-04-2012 |
3 | 150 | 07-05-2012 |
Result should look like this:
id | Weight_week1 | Weight_week2 | weight_week3 | weight_week4 |
1 | 120 | 100 | 130 | |
2 | 110 | 140 | | |
3 | 150 | | | |
Thanks in advance.
if this a single table then
SELECT GROUP_CONCAT(weight) as Weight,
WEEK(Created) as Week
Group by Week(Created)
This will give you a row each having week id and comma seperated whights
You could do it like this:
SELECT
t.id,
SUM(CASE WHEN WeekNbr=1 THEN Table1.Weight ELSE 0 END) AS Weight_week1,
SUM(CASE WHEN WeekNbr=2 THEN Table1.Weight ELSE 0 END) AS Weight_week2,
SUM(CASE WHEN WeekNbr=3 THEN Table1.Weight ELSE 0 END) AS Weight_week3,
SUM(CASE WHEN WeekNbr=4 THEN Table1.Weight ELSE 0 END) AS Weight_week4
FROM
(
SELECT
(
WEEK(Created, 5) -
WEEK(DATE_SUB(Created, INTERVAL DAYOFMONTH(Created) - 1 DAY), 5) + 1
)as WeekNbr,
Table1.id,
Table1.Weight,
Table1.Created
FROM
Table1
) AS t
GROUP BY
t.id
I don't know if you want a AVG,SUM,MAX or MIN but you can change the aggregate to what you want.
Useful references:
Function for week of the month in mysql
you cannot create fields on the fly like that but you can group them.
use GROUP_CONCAT to deliver results with a delimiter that you can separate on later.
You could also do this:
SELECT id, created, weight, (
SELECT MIN( created ) FROM weights WHERE w.id = weights.id
) AS `min` , round( DATEDIFF( created, (
SELECT MIN( created )
FROM weights
WHERE w.id = weights.id ) ) /7) AS diff
FROM weights AS w
ORDER BY id, diff
This code does not do pivot table. You should add some additional code to convert the data to your needs. You may run into trouble if you use WEEK() because of the years.

MySQL SUM in different ways

I have two tables
user_raters:
| id(int) | to_id(int) | value(int) | created_at(datetime)
|1 | 2 | 1 | 2009-03-01 00:00:00
EDIT: I changed the user_rater_id. history_user_raters.user_rater_id is related to user_raters.id
history_user_raters:
| id(int) | user_rater_id(int) | value(int) | created_at(datetime)
| 1 | 1 | 1 | 2009-03-02 00:00:00
| 2 | 1 | 1 | 2009-03-02 00:00:00
| 3 | 1 | -1 | 2009-03-02 00:00:00
| 4 | 1 | 1 | 2009-03-03 00:00:00
| 5 | 1 | -1 | 2009-03-03 00:00:00
| 6 | 1 | -1 | 2009-03-03 00:00:00
| 7 | 1 | -1 | 2009-03-03 00:00:00
I want to count the sum of the values from history_user_raters as it relates to the to_id from user_raters. The result from the query should be:
| year | month | day | total | down | up
| 2009 | 3 | 2 | 1 | 1 | 2
| 2009 | 3 | 3 | -2 | 3 | 1
I have a query that is close, but it is not counting the up and down correctly. The total is right. Can some one help me write the query or new query that calculates correct up and down?
My current query:
SELECT
YEAR(history.created_at) AS `year`,
MONTH(history.created_at) AS `month`,
DAY(history.created_at) AS `day`,
SUM(history.value) as `total`,
(SELECT
abs(SUM(historydown.value))
FROM `user_raters` as raterdown
INNER JOIN `history_user_raters` AS historydown
WHERE raterdown.to_id = 2
AND historydown.value = -1
AND date(historydown.created_at)
GROUP BY history.created_at) as down,
(SELECT SUM(historyup.value)
FROM `user_raters` as raterup
INNER JOIN `history_user_raters` AS historyup
WHERE raterup.to_id = 2
AND historyup.value = 1
AND date(history.created_at)
GROUP BY raterup.to_id) as up
FROM `user_raters`
INNER JOIN history_user_raters AS history ON user_raters.id = history.user_rater_id
WHERE (user_raters.to_id = 2)
GROUP BY DATE(history.created_at)
I might see it too simply (and sorry I can't test with data at the moment), but I'm guessing the following trick with two CASE statements would do just what is needed
SELECT
YEAR(history.created_at) AS year,
MONTH(history.created_at) AS month,
DAY(history.created_at) AS day,
SUM(history.value) as total,
SUM(CASE WHEN history.value < 0 THEN history.value ELSE 0 END) as down,
SUM(CASE WHEN history.value > 0 THEN history.value ELSE 0 END) as up
FROM `user_raters`
INNER JOIN `history_user_raters` AS history
ON user_raters.id = history.user_rater_id
WHERE (user_raters.to_id = 1) -- or some other condition...
GROUP BY DATE(history.created_at)
EDIT: #OMG Ponies deleted his answer. This response make no sense now, but I am not going to delete my answer, because I think it is silly.
#OMG ponies
Your query runs, but it returns no results. I had to adjust it a bit to add the to_id in the main queries where clause
SELECT
YEAR( t.created_at ) AS `year` ,
MONTH( t.created_at ) AS `month` ,
DAY( t.created_at ) AS `day` ,
SUM( t.value ) AS `total` ,
MAX( COALESCE( x.sum_down, 0 ) ) AS down,
MAX( COALESCE( y.sum_up, 0 ) ) AS up
FROM history_user_raters AS t
JOIN user_raters AS ur ON ur.to_id = t.user_rater_id
LEFT JOIN (
SELECT hur.user_rater_id,
SUM( hur.value ) AS sum_down
FROM history_user_raters AS hur
WHERE hur.value = -1
GROUP BY hur.user_rater_id
) AS x ON x.user_rater_id = t.user_rater_id
LEFT JOIN (
SELECT hur.user_rater_id,
SUM( hur.value ) AS sum_up
FROM history_user_raters AS hur
WHERE hur.value =1
GROUP BY hur.user_rater_id
) AS y ON y.user_rater_id = t.user_rater_id
WHERE ur.to_id =1
GROUP BY YEAR( t.created_at ) , MONTH( t.created_at ) , DAY( t.created_at )