Here is the my table :
mysql> select * from t1;
+------+-------+
| id | value |
+------+-------+
| 1 | 1 |
+------+-------+
1 row in set (0.00 sec)
mysql> select * from t2;
+------+-------+
| id | value |
+------+-------+
| 1 | 2 |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+------+-------+
4 rows in set (0.00 sec)
Then ,I run a sql to update the date in table t1 for some purpose:
mysql> update t1 join t2 on t1.id=t2.id set t1.value=t2.value ;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
And now,see the changes:
mysql> select * from t1;
+------+-------+
| id | value |
+------+-------+
| 1 | 2 |
+------+-------+
1 row in set (0.00 sec)
I wonder that why the rows matched count is 1 ,and it's hardly understand that the column value of t1 has a value 2 where the id=1 rather than 3.Is that update stops when it matches the first row ?
I think it will do a full data match across t1 and t2 in this case.
Any help is appreciated!
update
Thanks,here is the situation that I'm dealing with actully:
For the values in t2 ,concat them seperated by ',' and the merge into the value in table t1 group by each id ,But ,all the element in t1's value should be distinct.For example: as table t1 and t2 list above , after the update operation,the t1's value should be :"1,2,3",neither 2 nor 3 .
if I use the function groupconcat(),It's will be hard to make values to be distinct for t1's value.
Agin,I don't think it's clever to update only on row as in this case.If a update across multi tables ,all the rows matched by the join condition should be updated one by one in a loop.
Based on your update to your question you can do it like this
UPDATE t1 JOIN
(
SELECT id, GROUP_CONCAT(DISTINCT value ORDER BY value) value
FROM t2
GROUP BY id
) q
ON t1.id = q.id
SET t1.value = q.value
Outcome:
+------+-------+
| id | value |
+------+-------+
| 1 | 1,2,3 |
+------+-------+
Here is SQLFiddle demo
UPDATE: Based on your comments which changed your question again. To be able to update a delimited string of values in t1 based on values in t2 you you'll need help of a numbers(tally) table to split t1.value on the fly.
You can easily create such table like this
CREATE TABLE tally(n INT NOT NULL PRIMARY KEY);
INSERT INTO tally (n)
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 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) a
,(SELECT 0 AS N UNION ALL SELECT 1 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) b
ORDER BY n
That script creates a table with a sequence of numbers from 1 to 100 which will allow to effectively split up to 100 delimited values. If you need more or less you can easily adjust the script.
Now to update t1.value you can do
UPDATE t1 JOIN
(
SELECT id, GROUP_CONCAT(value ORDER BY value) value
FROM
(
SELECT id, SUBSTRING_INDEX(SUBSTRING_INDEX(t1.value, ',', n.n), ',', -1) value
FROM t1 CROSS JOIN tally n
WHERE n.n <= 1 + (LENGTH(t1.value) - LENGTH(REPLACE(t1.value, ',', '')))
UNION
SELECT id, value
FROM t2
) v
GROUP BY id
) q
ON t1.id = q.id
SET t1.value = q.value
Assuming that you have in t1
| ID | VALUE |
|----|-------|
| 1 | 1,4 |
outcome of the update will be
| ID | VALUE |
|----|---------|
| 1 | 1,2,3,4 |
Here is SQLFiddle demo
That all being said in the long run you better reconsider your db schema and normalize your data. That will pay off big time by allowing normally maintain and query your data.
What a weird query! It seems that MySQL is clever enough to not update the same row 4 times. But besides that, on any database the result (the new value for t1.value) is undefined. You should always make sure that you update with a value of one row, or use an aggregate function (like min, max, ...)
Related
While every question I found is talking about removing duplicates I need these duplicates.
Let's say my database is
+-------+-----------+
| ID | letter |
+-------+-----------+
| 1 | A |
| 2 | B |
| 4 | Z |
+-------+-----------+
I need to query a person name so let say the name is "ABA" when I query like this
select * from letters where letter = 'A' or letter = 'B' or letter = 'A'
My result will be
+-------+-----------+
| ID | letter |
+-------+-----------+
| 1 | A |
| 2 | B |
+-------+-----------+
I want the output will include the 3rd letter as a separate row.
+-------+-----------+
| ID | letter |
+-------+-----------+
| 1 | A |
| 2 | B |
| 3 | A |
+-------+-----------+
Maybe I don't know the right term but I didn't find even one answer that give me half a solution.
there is one entry but can I can the entry again? if I query for "nina" get the full name and not just "nia"
Original answer (and recommended approach)
Use a recursive query to convert the name into rows with one letter each. Then join with your letters table.
with recursive word_letters (word, pos, letter) as
(
select #name, 1, substr(#name, 1, 1)
union all
select word, pos + 1, substr(word, pos + 1, 1)
from word_letters
where pos < length(word)
)
select letters.*
from word_letters
join letters on letters.letter = word_letters.letter
order by word_letters.pos;
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=6547c21d2dd9223270615047d46d9783
UPDATE: Workaround for old MySQL versions
Build a table of positions (numbers) large enough to cover the longest word. Then join the word in order to get the position for each letter in it. Then join your other table.
select letters.*
from
(
select hundreds.digit * 100 + tens.digit * 10 + units.digit + 1 as pos
from (select 0 as digit union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) units
cross join (select 0 as digit union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) tens
cross join (select 0 as digit union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) hundreds
) positions
join (select #name as word) w on length(w.word) >= positions.pos
join letters on letters.letter = substr(w.word, positions.pos, 1)
order by positions.pos;
Demo: https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=db3762d5705ce5eb77e628c4d4058485
You can do it by using any of the below queries. For your reference visit the link below to test the output.
http://sqlfiddle.com/#!9/c5e9f3/1
The table created and the populated data is as below
create table tbl(ID int,letter varchar(2));
insert into tbl(ID,letter) values(1,'A');
insert into tbl(ID,letter) values(2,'B');
insert into tbl(ID,letter) values(3,'A');
insert into tbl(ID,letter) values(4,'Z');
insert into tbl(ID,letter) values(5,'C');
and now the query
select * from tbl where letter in ('A','B');
or
select * from tbl where letter ='A' or letter='B';
Is there a way to get MySQL 5.7/MariaDB to return 0 or 1 for rows that exist in a where IN() query?
like maybe SUM/IF/complex subquery SQL trickery? It could be the id or just a simple 1/0 to know which exists or not.
I thought if I could just add IFNULL(id,0) to a empty row it would return 4 results with 0, but it only shows 3:
Imagine a simple id table:
SELECT * FROM tbl;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.00 sec)
EXAMPLE QUERY:
SELECT IFNULL(id,0) FROM tbl WHERE id in (1,2,3,4);
EXPECTED RESULTS
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 0 |
------
4 results
ACTUAL
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.01 sec)
--------
Basically what I'm trying to do is to return both the existing id and empty ids and not use [temporary] tables, it could be a complex subquery using IN() or FIND_IN_SET or combination of where IN() and EXISTS or NOT EXISTS, etc, etceven a stored procedure, or possibly JSON trickery, etc.
Your IN criteria needs to exist as rows for the result to be rows. So:
SELECT coalesce(id, 0)
FROM
(SELECT 1 AS e
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4) t2
LEFT JOIN tbl ON id=e
fiddle
If its always a sequence, mariadb's in-build sequence engine creates magic tables of sequences:
SELECT coalesce(id, 0)
FROM seq_1_to_4 t2
LEFT JOIN tbl ON id=seq
fiddle
If the ids are actually all some other table, select from that and left join tbl. If not, emulate such a table:
select in_ids.id, tbl.id is not null as id_exists
from (select 0 id union all select 1 union all select 2 union all select 3) in_ids
left join tbl on tbl.id=in_ids.id
If there is some rule (all integers in a range, for instance) in MySQL 8 or mariadb 10.2, you would use a recursive common table expression instead:
with recursive in_ids as (
select 1 as id
union all
select id+1 from in_ids
where id < 4
)
select in_ids.id, tbl.id is not null as id_exists
from in_ids
left join tbl on tbl.id=in_ids.id
You can also generate a sequence large enough to cover your possible range and then keep your IN to only look at particular values (here, from 1 to 1024):
select sequence.n, tbl.id is not null as id_exists
from (
select 1+i+4*(j+4*(k+4*(l+4*m))) n
from (select 0 i union all select 1 union all select 2 union all select 3) i
cross join (select 0 j union all select 1 union all select 2 union all select 3) j
cross join (select 0 k union all select 1 union all select 2 union all select 3) k
cross join (select 0 l union all select 1 union all select 2 union all select 3) l
cross join (select 0 m union all select 1 union all select 2 union all select 3) m
) sequence
left join tbl on tbl.id=sequence.n
where sequence.n in (1,2,3,4);
or with CTEs:
with recursive sequence as (
select 1 as n
union all
select n+1 from sequence
where n < 1024
)
left join tbl on tbl.id=sequence.n
where sequence.n in (1,2,3,4);
This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 5 years ago.
I have this table
id|name|number|date
1|peter|2|2017-09-18
2|peter|1|2017-10-03
3|james|4|2017-09-05
4|james|1|2017-10-10
5|james|0|2017-10-15
6|kate|4|2017-09-16
7|kate|2|2017-10-17
I want to select the latest row for each person (the row with the latest date). The result will look like
2|peter|1|2017-10-03
5|james|0|2017-10-15
7|kate|2|2017-10-17
Which MYSQL query can do that?
Something like :
SELECT id, name, number, date
FROM table
GROUP BY id, name, number, date
HAVING date the latest
MariaDB [test]> select * from stack s1 where s1.d = (select max(s2.d)
from stack s2 where s2.name = s1.name);
+------+-------+------+------------+
| id | name | num | d |
+------+-------+------+------------+
| 2 | peter | 1 | 2017-10-03 |
| 5 | james | 0 | 2017-10-15 |
| 7 | kate | 2 | 2017-10-17 |
+------+-------+------+------------+
3 rows in set (0.00 sec)
And for large datasets, don't forget to add indexes to 'name' and 'd' columns.
SELECT * FROM persons WHERE name='someone' order by date desc limit 1
This works well for selecting lastest entity by specified name.
SELECT * FROM persons GROUP BY name, date ORDER BY date DESC
I wrote this with my mobile, something can be wrong, but its concepts will be correct.
select t1.* from table t1, (select t2.name name2, max(t2.date)
date2 from table t2 group by t2.name) t3 where t1.name = t3.name2 and t1.date = t3.date2
Considering this table:
+-----+--------+
| id | value |
+-----+--------+
| 1 | 22 |
+-----+--------+
| 2 | 12 |
+-----+--------+
| 3 | 22 |
+-----+--------+
| 4 | 22 |
+-----+ -------+
I can select all where the column value is duplicated like so:
select value from table having count(value) > 1 ;
This will output the Ids 1,3 and 4.
What I'm attempting to do is select where duplicates, but leaving 1 (one) duplicate un selected, so the above would output only the Ids 3 and 4 (or 1 and 3 etc... the duplicate omitted does not matter, only that it is.
How can I achieve this?
This question IS NOT a duplicate of
Using LIMIT within GROUP BY to get N results per group?
You could use an aggregatio function for filter a value for id and the select all the others
select * from table
where (value, id) not in (
select value, max(id)
from table
group by value
having count(value) > 1
)
;
You can do either as:
select *
from test t1
where exists (select 1
from test t2
where t2.value = t1.value
having count(value)>1)
limit 2
OR:
select t1.*
from test t1 inner join
(select value from test t2 having count(value)>1) t2
on t1.value = t2.value
limit 2;
I am doing the next query:
SELECT id, name, keyt
FROM table
WHERE id = (SELECT t2.id FROM table t2 WHERE t2.keyt=21 ORDER BY RAND() LIMIT 1)
Supposing table is like this:
| id | name | keyt |
+ ------------------------- +
| 1 | Hello | 21 |
| 3 | Katzet | 1 |
| 1 | Welcome | 1 |
| 2 | Two | 21 |
| 2 | Other | 1 |
It should return one of this pairs:
Hello | Welcome (id 1 in common)
Two | Other (id 2 in common)
So, the idea is:
Get one id, which has the keyt value set to 21
Then, get all the rows with this selected id (independently of all the other keyt values)
If I do as you suggested... I would get mixed id values, and all result rows must have the same id.
SELECT x.*
FROM my_table x
JOIN
( SELECT id
FROM my_table
WHERE keyt = 21
ORDER
BY RAND() LIMIT 1
) y
ON y.id = x.id;
The subquery in this query
SELECT id, name, keyt
FROM table
WHERE id = (SELECT t2.id FROM table t2 WHERE t2.keyt=21 ORDER BY RAND() LIMIT 1)
would return only one record as it has LIMIT 1 added at the end.
Also, in your question, the table contains only 1 record for which
value of keyt = 21, due to which you're getting only one record.
If you want more records, you should remove the LIMIT. In that case you may rephrase your query as:
SELECT id, name, keyt
FROM table
WHERE id IN (SELECT t2.id FROM table t2 WHERE t2.keyt=21 ORDER BY RAND())
Hope this is what you expected. As your actual goal is not very clear from the question.
Your table has two 21 in the keyt column so your subquery in the where clause returns 2 values if id that is 1 and 2.So what you need to do is instead of using an equal to operator "=" use IN operator in the where clause.
SELECT id, name, keyt FROM table WHERE id IN (SELECT t2.id FROM table t2 WHERE t2.keyt=21 ORDER BY RAND())