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.
Related
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;
I'm having a below table called members
+----+--------+--------+-------+--+
| id | name | gender | state | |
+----+--------+--------+-------+--+
| 1 | Peter | M | 1 | |
+----+--------+--------+-------+--+
| 2 | Sally | F | 1 | |
+----+--------+--------+-------+--+
| 3 | Martin | M | 0 | |
+----+--------+--------+-------+--+
Then, my query is
SELECT * FROM `member` WHERE `id` = '1 3';
MySQL returned | 1 | Peter | M | 1 | |.
However, I expect that it would return nothing because there should be no record with id = '1 3'.
How can I prevent this?
Try to use BINARY
select * from member where BINARY id = '1 3';
Presume first, that the following table exists in a MySQL Database
|----|-----|-----|----|----|-----------|--------------|----|
| id | rid | ver | n1 | n2 | s1 | s2 | b1 |
|----|-----|-----|----|----|-----------|--------------|----|
| 1 | 1 | 1 | 0 | 1 | Hello | World | 0 |
| 2 | 1 | 2 | 1 | 1 | Hello | World | 0 |
| 3 | 1 | 3 | 0 | 0 | Goodbye | Cruel World | 0 |
| 4 | 2 | 1 | 0 | 0 | Hello | Doctor | 1 |
| 5 | 2 | 2 | 0 | 0 | Hello | Nurse | 1 |
| 6 | 3 | 1 | 0 | 0 | Dippity | Doo-Dah | 1 |
|----|-----|-----|----|----|-----------|--------------|----|
Question
How do I write a query to determine whether for any given rid, what changed between the most recent version and the version immediately preceding it (if any) such that it produces something like this:
|-----|-----------------|-----------------|-----------------|
| rid | numbers_changed | strings_changed | boolean_changed |
|-----|-----------------|-----------------|-----------------|
| 1 | TRUE | TRUE | FALSE |
| 2 | FALSE | TRUE | FALSE |
| 3 | n/a | n/a | n/a |
|-----|-----------------|-----------------|-----------------|
I think that I should be able to do this by doing a cross-join between the table and itself but I can't resolve how to perform this join to get the desired output.
I need to generate this "report" for a table with 10's of columns and 1-10 versions of 100's of records (resulting in 1000's of rows). Note the particular design of the database is not my own and altering the structure of the database (at this time) is not an acceptable approach.
The actual format of the output isn't important - and if it simplifies the query getting a "full breakdown" of what changed for each "change set" would also be acceptable, for example
|-----|-----|-----|----|----|----|----|----|
| rid | old | new | n1 | n2 | s1 | s2 | b1 |
|-----|-----|-----|----|----|----|----|----|
| 1 | 1 | 2 | Y | N | N | N | N |
| 1 | 2 | 3 | Y | Y | Y | Y | N |
| 2 | 4 | 5 | N | N | N | Y | N |
|-----|-----|-----|----|----|----|----|----|
Note that it is also ok, in this case to omit rid records which only have a single version, as for the purposes of this report I only care about records that have changed and getting a separate list of records that haven't changed is an easy query
You can join every row with the following one with
select *
from history h1
join history h2
on h2.rid = h1.rid
and h2.id = (
select min(h.id)
from history h
where h.rid = h1.rid
and h.id > h1.id
);
Then you just need to compare every column from the two rows like h1.n1 <> h2.n1 as n1.
The full query would be:
select h1.rid, h1.id as old, h2.id as new
, h1.n1 <> h2.n1 as n1
, h1.n2 <> h2.n2 as n2
, h1.s1 <> h2.s1 as s1
, h1.s2 <> h2.s2 as s2
, h1.b1 <> h2.b1 as b1
from history h1
join history h2
on h2.rid = h1.rid
and h2.id = (
select min(h.id)
from history h
where h.rid = h1.rid
and h.id > h1.id
);
Result:
| rid | old | new | n1 | n2 | s1 | s2 | b1 |
|-----|-----|-----|----|----|----|----|----|
| 1 | 1 | 2 | 1 | 0 | 0 | 0 | 0 |
| 1 | 2 | 3 | 1 | 1 | 1 | 1 | 0 |
| 2 | 4 | 5 | 0 | 0 | 0 | 1 | 0 |
Demo: http://sqlfiddle.com/#!9/2e5d12/5
If the columns can contain NULLs, You might need something like NOT h1.n1 <=> h2.n1 as n1. <=> is a NULL-save equality check.
If the version within a rid group is guaranteed to be consecutive, you can simplify the JOIN to
from history h1
join history h2
on h2.rid = h1.rid
and h2.ver = h1.ver + 1
Demo: http://sqlfiddle.com/#!9/2e5d12/7
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.
translations
+---------+----------------+----------+---------+
| id_user | id_translation | referrer | id_word |
+---------+----------------+----------+---------+
| 1 | 3 | NULL | 4 |
| 1 | 17 | NULL | 3 |
| 2 | 17 | NULL | 5 |
| 2 | 17 | NULL | 1 |
| 2 | 17 | NULL | 7 |
words
+----+------+
| id | word |
+----+------+
| 4 | out |
+----+------+
users_translations
+---------+----------------+----------+---------+
| id_user | id_translation | referrer | id_word |
+---------+----------------+----------+---------+
| 1 | 17 | 1 | 4 |
| 2 | 17 | 2 | 4 |
| 3 | 18 | NULL | 4 |
I need to select all translations for current word and id_translation, but if in the row referrer = 1 (current user), then I don't need another results (translations from another users for current word), if there is no referrer = 1, show all.
SELECT DISTINCT `t`.*, `ut`.`id_user` AS tuser
FROM translations AS t
LEFT JOIN users_translations AS ut ON `t`.`id` = `ut`.`id_translation`
INNER JOIN words ON `words`.`id` = `ut`.`id_word` OR `words`.`id` = `t`.`id_word`
WHERE (`word` = 'help')
ORDER BY `t`.`translation` ASC
+----+-------------+---------+---------+-------+
| id | translation | id_word | id_user | tuser |
+----+-------------+---------+---------+-------+
| 17 | допомагати | 4 | 1 | 2 |
| 17 | допомагати | 4 | 1 | 1 |
First row doesn't need, because we have tuser = 1. When there is no tuser = 1, all results should be returned.
I don't understand how to build select statement and I will be very appreciative that somebody shows me how to make it work.
First thing that comes to mind
--add this to your where clause
id_user <=
CASE WHEN EXISTS(SELECT * FROM translations WHERE id_user = 1 AND id_word = words.id_word)
THEN 1
ELSE (SELECT MAX(Id) FROM translations)
END