SQL update after choosing a MAX from some AVERAGEs - mysql

I have 2 tables with same columns but different data. I need to compute the average of a column in one table ( with some filters ) and to choose the MAX of them. Then to put that value in the 2nd table.
I've built so far this query:
UPDATE st16
INNER JOIN st17 ON st17.parent = st16.uid
SET
st16.p1 = SELECT MAX(
(SELECT AVG(st17.p1) FROM st17 WHERE st17.parent = st16.uid AND st17.row = st16.row)),
st16.p2 = SELECT MAX(
(SELECT AVG(st17.p2) FROM st17 WHERE st17.parent = st16.uid AND st17.row = st16.row))
but I get this error: "#1111 - Invalid use of group function".
Any ideas? Thanks!
Sample data ( first is st17, and below is st16 ):
+----------------------------------+----------------------------------+----------------------------------------------------------------------------------------------------------------+---------------------+---------------------+-----+
| uid | parent | fen | p1 | p2 | row |
+----------------------------------+----------------------------------+----------------------------------------------------------------------------------------------------------------+---------------------+---------------------+-----+
| ee95b564f2b3fa1573b451d8f4e00f5d | bc5ef0d66b3bde08b0ba35a91412c058 | QS7D8D/4H9HQH4D4S/6H8HTHJHKH/4CAS/9S9D7CJC9C/6C8CQCKCAC/6D5D3D2DKSJSTS8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD | -10.481481481481481 | 10.481481481481481 | 1 |
| 691ed545dd5375cb3e75f0b8d032534b | bc5ef0d66b3bde08b0ba35a91412c058 | QS7D6D/4H9HQH4D4S/6H8HTHJHKH/4CAS/9S9D7CJC9C/6C8CQCKCAC/5D3D2DKSJSTS8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D | -10.481481481481481 | 10.481481481481481 | 1 |
| b6e2a3f4ea51c8e6638a2cc657bf3511 | bc5ef0d66b3bde08b0ba35a91412c058 | QS7D5D/4H9HQH4D4S/6H8HTHJHKH/4CAS/9S9D7CJC9C/6C8CQCKCAC/3D2DKSJSTS8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D6D | -10.481481481481481 | 10.481481481481481 | 1 |
| 0dbe5038d01e457e4f65415ac081d0dd | bc5ef0d66b3bde08b0ba35a91412c058 | QS7D3D/4H9HQH4D4S/6H8HTHJHKH/4CAS/9S9D7CJC9C/6C8CQCKCAC/2DKSJSTS8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D6D5D | -10.481481481481481 | 10.481481481481481 | 1 |
| ca1e85058ed8294d60a9922d36f8c1fa | bc5ef0d66b3bde08b0ba35a91412c058 | QS7D2D/4H9HQH4D4S/6H8HTHJHKH/4CAS/9S9D7CJC9C/6C8CQCKCAC/KSJSTS8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D6D5D3D | -10.481481481481481 | 10.481481481481481 | 1 |
| e85179f395ba8e441ff7b1544e05404c | c75eb9315dee4e3b42fb52e8cd509910 | QS7DJS/4H9HQH4D4S/6H8HTHJHKH/4CKS/9S9D7CJC9C/6C8CQCKCAC/TS8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D6D5D3D2DAS | -9.703703703703704 | 9.703703703703704 | 1 |
| eb3c352febe8ff25f375032bbb6cc5d7 | c75eb9315dee4e3b42fb52e8cd509910 | QS7DTS/4H9HQH4D4S/6H8HTHJHKH/4CKS/9S9D7CJC9C/6C8CQCKCAC/8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D6D5D3D2DASJS | -9.703703703703704 | 9.703703703703704 | 1 |
| 69f06801edf9b3cf669df56dc9152271 | c75eb9315dee4e3b42fb52e8cd509910 | QS7D8S/4H9HQH4D4S/6H8HTHJHKH/4CKS/9S9D7CJC9C/6C8CQCKCAC/7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D6D5D3D2DASJSTS | -9.703703703703704 | 9.703703703703704 | 1 |
| 5f78082dd3aee8b51bf096286df5e4e7 | c75eb9315dee4e3b42fb52e8cd509910 | QS7D5H/4H9HQH4D4S/6H8HTHJHKH/4CKS/9S9D7CJC9C/6C8CQCKCAC/3H2HTC5C3C2CADKDQDJDTD8D6D5D3D2DASJSTS8S7S6S5S3S2SAH7H | -9.703703703703704 | 9.703703703703704 | 1 |
| 7ee50e8aa1afd3af703b3a5b3cdf3cf8 | c75eb9315dee4e3b42fb52e8cd509910 | QS7D3H/4H9HQH4D4S/6H8HTHJHKH/4CKS/9S9D7CJC9C/6C8CQCKCAC/2HTC5C3C2CADKDQDJDTD8D6D5D3D2DASJSTS8S7S6S5S3S2SAH7H5H | -9.703703703703704 | 9.703703703703704 | 1 |
+----------------------------------+----------------------------------+----------------------------------------------------------------------------------------------------------------+---------------------+---------------------+-----+
+----------------------------------+----------------------------------+----------------------------------------------------------------------------------------------------------------+----+----+-----+
| uid | parent | fen | p1 | p2 | row |
+----------------------------------+----------------------------------+----------------------------------------------------------------------------------------------------------------+----+----+-----+
| bc5ef0d66b3bde08b0ba35a91412c058 | 9e123e356e468b847d4493cf55809fcd | QS7D/4H9HQH4D4S/6H8HTHJHKH/4CAS/9S9D7CJC9C/6C8CQCKCAC/KSJSTS8S7S6S5S3S2SAH7H5H3H2HTC5C3C2CADKDQDJDTD8D6D5D3D2D | 0 | 0 | 1 |
+----------------------------------+----------------------------------+----------------------------------------------------------------------------------------------------------------+----+----+-----+

As Gordon Linoff mentioned, you can't pass a subquery to an aggregate function such as MAX(). Another problem that you will likely run into: you cannot select from the table you are updating in MySQL. So something like this
UPDATE st16
SET
st16.p1 = (SELECT AVG(st17.p1) FROM st17 JOIN st16 ON st17.parent = st16.uid WHERE st17.row = st16.row ORDER BY AVG(st17.p1) DESC LIMIT 1),
st16.p2 = (SELECT AVG(st17.p2) FROM st17 JOIN st16 ON st17.parent = st16.uid WHERE st17.row = st16.row ORDER BY AVG(st17.p2) DESC LIMIT 1);
will not work, unfortunately. You might just want to break this into multiple queries; that is, retrieve the maximum averages first in a SELECT, then ship those results in a second, separate UPDATE.

Related

Remove duplicates leaving at least one with highest parameter from group

I have following schema:
+--+------+-----+----+
|id|device|token|cash|
+--+------+-----+----+
column device is unique and token is not unique and null by default.
What i want to achieve is to set all duplicate token values to default (null) leaving only one with highest cash. If duplicates have same cash leave first one.
I have heard about cursor, but it seems that it can be done with usual query.
I have tried following SELECT only to see if im right about my thought how to achieve this, but it seems im wrong.
SELECT
*
FROM
db.table
WHERE
db.table.token NOT IN (SELECT
*
FROM
(
SELECT DISTINCT
MAX(db.table.balance)
FROM
db.table
GROUP BY db.table.balance) temp
)
For example:
This table after query
+-----+---------+--------+-------+
| id | device | token | cash|
+-----+---------+--------+-------+
| 1 | dev_1 | tkn_1 | 3 |
| 2 | dev_2 | tkn_1 | 10 |
| 3 | dev_3 | tkn_2 | 10 |
| 4 | dev_4 | tkn_2 | 14 |
| 5 | dev_5 | tkn_3 | 10 |
| 6 | dev_6 | null | 10 |
| 7 | dev_7 | null | 10 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | tkn_5 | 11 |
+-----+---------+--------+-------+
should be:
+-----+---------+--------+-------+
| id | device | token | cash|
+-----+---------+--------+-------+
| 1 | dev_1 | null | 3 |
| 2 | dev_2 | tkn_1 | 10 |
| 3 | dev_3 | null | 10 |
| 4 | dev_4 | tkn_2 | 14 |
| 5 | dev_5 | tkn_3 | 10 |
| 6 | dev_6 | null | 10 |
| 7 | dev_7 | null | 10 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | null | 11 |
| 8 | dev_8 | tkn_5 | 15 |
+-----+---------+--------+-------+
Thanks in advance :)
Try using an EXISTS subquery:
UPDATE yourTable t1
SET token = NULL
WHERE EXISTS (SELECT 1 FROM (SELECT * FROM yourTable) t2
WHERE t2.token = t1.token AND
t2.cash > t1.cash);
Demo
Note that this answer assumes that there would never be a tie for two token records having the same highest cash amount.
To set exactly one row in the even of duplicates on the maximum cash, use the id:
update t join
(select tt.*,
(select t3.id
from t t3
where t3.token = tt.token
order by t3.cash desc, id desc
) as max_cash_id
from t tt
) tt
on t.id = tt.id and t.id < tt.max_cash_id
set token = null;

Hierarchical query in MySQL

Having that table structure & data:
| ID | PARENT | FIELD_1 | FIELD_2 | RATING |
+------------------------------------------+
| 1 | NULL | F1V1 | F2V1 | 10 |
| 2 | 1 | F1V2 | F2V2 | 20 |
| 3 | 2 | F1V3 | F2V3 | 30 |
| 4 | 3 | F1V4 | F2V4 | 40 |
Is there a way of getting a result like this one:
| ID | F_1 | F_2 | P_F_1 | P_F_2 | G_F_1 | G_F_2 | S_R |
+-------------------------------------------------------------+
| 1 | F1V1 | F2V1 | NULL | NULL | NULL | NULL | 10 |
| 2 | F1V2 | F2V2 | F1V1 | F2V1 | NULL | NULL | 30 |
| 3 | F1V3 | F2V3 | F1V2 | F2V2 | F1V1 | F2V1 | 60 |
| 4 | F1V4 | F2V4 | F1V3 | F2V3 | F1V2 | F2V2 | 90 |
What I actually want, as you can see, is for every record if there are parent (P), grandparent (G), etc records (the recursion may go for 4 levels or any other finite number that is already known), the fields of their ancestors prefixed (that can happen programmatically outside of the query) and a SUM (or any other GROUP function) that calculates the values recursively as well.
ex record #4:
ID = 4
FIELD_1 AS F_1 = F1V4
FIELD_2 AS F_2 = F2V4
PARENT_FIELD_1 AS P_F_1 = F1V3
...
GRANDPARENT_FIELD_2 AS G_F_2 = F2V2
SUM_RATING AS S_M = (40 + 30 + 20) = 90**
NOTE:
Even though record #1 is an ancestor of record #4 (grand-grandparent) its rating is not calculated in the sum because it is not contained in the query.
This simplest way:
SELECT t.id,
t.field_1 f_1,
t.field_2 f_2,
p.field_1 p_f_1,
p.field_2 p_f_2,
g.field_1 g_f_1,
g.field_2 g_f_2,
t.rating + COALESCE(p.rating,0) + COALESCE(g.rating,0) s_r
FROM table_name t
LEFT JOIN table_name p
ON p.id = t.parent
LEFT JOIN table_name g
ON g.id = p.parent
And add left joins, additions and field selections to the known level of recursion.

COUNT reducing results?

A query without COUNT returns 3 records, with only 1.
SELECT `blog_cate` . * , COUNT( blogi.blog_cate ) AS num
FROM (
`blog_cate`
)
JOIN `blogi` ON `blogi`.`blog_cate` = `blog_cate`.`blogi_cate_url`
results:
+----+------------------+----------------+-----+
| id | blogi_cate_title | blogi_cate_url | num |
+----+------------------+----------------+-----+
| 1 | Базы данных | batabase | 3 |
+----+------------------+----------------+-----+
And the same query, but without a COUNT:
SELECT `blog_cate` . *
FROM (
`blog_cate`
)
JOIN `blogi` ON `blogi`.`blog_cate` = `blog_cate`.`blogi_cate_url`
That returns me 3 records:
+----+------------------+----------------+
| id | blogi_cate_title | blogi_cate_url |
+----+------------------+----------------+
| 1 | Базы данных | batabase |
| 1 | Базы данных | batabase |
| 3 | Разработка | razrabotka |
+----+------------------+----------------+
Is it possible to use a COUNT and have a normal results?
p.s. tables:
+----+------------+
| id | blog_cate |
+----+------------+
| 1 | batabase |
| 2 | batabase |
| 3 | razrabotka |
+----+------------+
+----+------------------+----------------+
| id | blogi_cate_title | blogi_cate_url |
+----+------------------+----------------+
| 1 | Базы данных | batabase |
| 2 | PHP | php |
| 3 | Разработка | razrabotka |
+----+------------------+----------------+
COUNT() with out a group by will group all records and produce a count of them. Adding more fields to the select will only show the details of the first record
You could build one query to get the three rows and one query to get the count result and join them via cross join to combine every detail row with the count row.

MySQL - Time Series Sliding Window

I have a MySQL table containing financial market prices.
+------------+------+--------+--------+--------+--------+
| date | pair | open | high | low | close |
+------------+------+--------+--------+--------+--------+
| 12/9/2009 | 1 | 1.4703 | 1.4783 | 1.4668 | 1.4727 |
| 12/9/2009 | 2 | 1.6287 | 1.6378 | 1.6167 | 1.6262 |
| 12/9/2009 | 3 | 0.9038 | 0.9116 | 0.9015 | 0.9086 |
| 12/9/2009 | 4 | 88.435 | 88.71 | 87.36 | 87.865 |
| 12/9/2009 | 5 | 1.064 | 1.0664 | 1.0515 | 1.0545 |
| 12/10/2009 | 1 | 1.4725 | 1.4761 | 1.4683 | 1.4732 |
| 12/10/2009 | 2 | 1.6261 | 1.6348 | 1.6214 | 1.6279 |
| 12/10/2009 | 3 | 0.9086 | 0.9192 | 0.908 | 0.9166 |
| 12/10/2009 | 4 | 87.87 | 88.47 | 87.73 | 88.2 |
| 12/10/2009 | 5 | 1.0546 | 1.0584 | 1.0479 | 1.0517 |
| 12/11/2009 | 1 | 1.4733 | 1.4778 | 1.4586 | 1.4615 |
| 12/11/2009 | 2 | 1.6278 | 1.634 | 1.6197 | 1.6262 |
| 12/11/2009 | 3 | 0.9164 | 0.9197 | 0.909 | 0.9128 |
| 12/11/2009 | 4 | 88.2 | 89.82 | 88.195 | 89.115 |
| 12/11/2009 | 5 | 1.0517 | 1.0624 | 1.0483 | 1.0602 |
+------------+------+--------+--------+--------+--------+
I want to get something like this. This is filtered by pair (where pair = 1). Every row consists of two consecutive rows.
+--------+--------+--------+--------+--------+--------+--------+--------+
| open1 | high1 | low1 | close1 | open2 | high2 | low2 | close2 |
+--------+--------+--------+--------+--------+--------+--------+--------+
| 1.4703 | 1.4783 | 1.4668 | 1.4727 | 1.4725 | 1.4761 | 1.4683 | 1.4732 |
| 1.4725 | 1.4761 | 1.4683 | 1.4732 | 1.4733 | 1.4778 | 1.4586 | 1.4615 |
+--------+--------+--------+--------+--------+--------+--------+--------+
I tried this query from https://stackoverflow.com/a/5084722/1487781 to get two consecutive dates.
select (
select max(t1.date)
from data as t1
where t1.date < t2.date
and t1.pair = 1
) as date1,
t2.date as date2
from data as t2
It worked but I can't rewrite it to suit my need as I need values and I can't just use max() to do that. Also I need to know how to generalize the solution. For example how if I need three or four consecutive rows.
Try this query:
SELECT d1.date date1,
d2.date date2,
d1.pair,
d1.open open1,
d1.high high1,
d1.low low1,
d1.close close2,
d2.open open2,
d2.high high2,
d2.low low2,
d2.close close2
FROM table1 d1
JOIN table1 d2
ON d1.pair = d2.pair
AND d1.date = d2.date - interval 1 day
Demo: http://www.sqlfiddle.com/#!2/f490d/2
Here is a version with a subquery that determines a next date for given pair number (next date = lowest date that is greater than given date):
SELECT d1.date date1,
d2.date date2,
d1.pair,
d1.open open1,
d1.high high1,
d1.low low1,
d1.close close2,
d2.open open2,
d2.high high2,
d2.low low2,
d2.close close2
FROM table1 d1
JOIN table1 d2
ON d1.pair = d2.pair
AND d2.date = (
SELECT min(date)
FROM table1 t
WHERE t.date > d1.date
AND t.pair = d1.pair
)
demo: --> http://www.sqlfiddle.com/#!2/f490d/9

How do I properly format this MySQL JOIN Statement?

I've got a table that looks like:
Table 1 ->
+----+--------+--------+
| id | name | author |
+----+--------+--------+
| 1 | First | Me |
| 2 | Second | You |
+----+--------+--------+
Table 2 ->
+-----+------------+-----------+------------+
| mid | table1_id | key | value |
+-----+------------+-----------+------------+
| 1 | 1 | desc | hello |
| 2 | 1 | begin_day | monday |
| 3 | 1 | end_day | tuesday |
| 4 | 2 | desc | goodbye |
| 5 | 2 | begin_day | wednesday |
| 6 | 2 | end_day | friday |
+-----+------------+-----------+------------+
The relationship here is that the id in table 1 corresponds to the table1_id in table 2.
The output that I'm trying to get is...
+----+---------+---------+-------------+-----------+-----------+
| id | name | author | desc | begin_day | end_day |
+----+---------+---------+-------------+-----------+-----------+
| 1 | First | Me | hello | monday | tuesday |
| 1 | Second | You | goodbye | wednesday | friday |
+----+---------+---------+-------------+-----------+-----------+
I've tried several different join statements -- all a variation of the below. I'm not that well versed in MySQL queries, however.
SELECT * FROM table_1 LEFT JOIN table_2 on table_1.id = table_2.table1_id
Which produces...
+----+----------+----------+----------+------------+-----------+
| id | mid | name | author | key | value |
+----+----------+----------+----------+------------+-----------+
| 1 | 1 | First | Me | desc | hello |
| 1 | 2 | First | Me | begin_day | monday |
| 1 | 3 | First | Me | end_day | tuesday |
| 2 | 4 | Second | You | desc | goodbye |
| 2 | 5 | Second | You | begin_day | wednesday|
| 2 | 6 | Second | You | end_day | friday |
Obviously, iterating over this join statement produces 6 results, 1 for each row in table 2 that matches the id in table 1. How can I avoid this with a proper query statement?
Thank you in advance.
You can use a case statement if you know all of the columns you will be getting, as follows:
Select distinct table_1.*,
case when table_2.key='desc' then value end as desc,
case when table_2.key='begin_day' then value end as begin_day,
case when table_2.key='end_day' then value end as end_day
FROM table_1 LEFT JOIN table_2 on table_1.id = table_2.table1_id
Hope this helps!
SELECT
table_1.*,
MAX(IF(key='desc', value, NULL)) AS 'desc',
MAX(IF(key='begin_day', value, NULL)) AS begin_day,
MAX(IF(key='end_day', value, NULL)) AS end_day
FROM table_1
LEFT JOIN table_2 ON (id = table1_id)
GROUP BY id;