MySql join two tables sequentially - mysql

Table one
===================
id name
-------------------
1 m
2 m
3 a
4 u
5 g
Table two
===================
id name
-------------------
8 m
9 m
10 u
11 a
12 x
15 m
Expected result
===================
1 m 8
2 m 9
3 a 11
4 u 10
I need to find id from table 2 associated with table 1 by name. But ids from table 2 must be different.
If i make join i receive wrong intersections:
select t1.id as i1, t1.name, t2.id as i2 from t1
join t2 on t1.name = t2.name
i1 name i2
--------------------
'1','m','8'
'2','m','8'
'1','m','9'
'2','m','9'
'4','u','10'
'3','a','11'
'1','m','15'
'2','m','15'
I need this for tables synchronization from different systems.

You can use the following query:
SELECT t1.id, t1.name, t2.id
FROM (
SELECT id, name,
#rn1 := IF(#n = name, #rn1 + 1,
IF(#n := name, 1, 1)) AS rn1
FROM Table1
CROSS JOIN (SELECT #rn1 := 0, #n := '') AS vars
ORDER BY name, id) AS t1
INNER JOIN (
SELECT id, name,
#rn2 := IF(#n = name, #rn2 + 1,
IF(#n := name, 1, 1)) AS rn2
FROM Table2
CROSS JOIN (SELECT #rn2 := 0, #n := '') AS vars
ORDER BY name, id
) AS t2 ON t1.name = t2.name AND t1.rn1 = t2.rn2
ORDER BY t1.id
The query uses variables in order to simulate ROW_NUMBER() window function, currently not available in MySQL. Variables #rn1, #rn2 enumerate records that belong to the same name partition with an order determined by id field.
Demo here

Related

How I can mix column data

I have a table table_1
id contact_id
1 500
5 89
8 35
15 458
... ...
555 38
how I can mix contact_id, result for table_1
id contact_id
1 35
5 458
8 35
15 89
... ...
555 45
You can randomly assign the contact ids using variables or row_number() (in MySQL 8+):
select t1.id, tt1.contact_id
from (select t1.*, row_number() over (order by id) as seqnum
from table_1 t1
) t1 join
(select t1.*, row_number() over (order by rand()) as seqnum
from table_1 t1
) tt1
on t1.seqnum = tt1.seqnum;
Without much difficulty (but more typing), this can be transformed to use variables in earlier versions.
You can also incorporate this into an update statement, if you want to shuffle the values permanently.
EDIT:
I think you want:
update table1 t1 join
(select t1.id, tt1.contact_id
from (select t1.*, (#rn1 := #rn1 + 1) as seqnum
from (select * table_1 order by id) t1 cross join
(select #rn1 := 0) params
) t1 join
(select t1.*, (#rn2 := #rn2 + 1) as seqnum
from (select * from table_1 order by rand()) t1 cross join
(select #rn2 := 0) params
) tt1
on t1.seqnum = tt1.seqnum
) tt1
on tt1.id = t1.id
set t1.contact_id = tt1.contact_id;

how to port this postgresql lag statement to mysql?

Imagine a table just filled with ID's and created timestamps, how would I convert this over to MySQL?:
SELECT created AS col_a , LAG (created) OVER ( ORDER by created ) AS col_b
FROM tester
You can use a correlated subquery:
SELECT t1.created AS col_a,
(SELECT created
FROM tester AS t2
WHERE t2.created < t1.created
ORDER BY created DESC LIMIT 1) AS col_b
FROM tester AS t1
or, use variables:
SELECT t1.created AS col_a, t2.created AS col_b
FROM (
SELECT created, #rn1 := #rn1 + 1 AS rn
FROM tester
CROSS JOIN (SELECT #rn1 := 0) AS var
ORDER BY created) AS t1
LEFT JOIN (
SELECT created, #rn2 := #rn2 + 1 AS rn
FROM tester
CROSS JOIN (SELECT #rn2 := 0) AS var
ORDER BY created
) AS t2 ON t1.rn = t2.rn + 1

Update duplicate rows

I have a table:
id name
1 a
2 a
3 a
4 b
5 b
6 c
I am looking for an update statement that will update name column to:
id name
1 a
2 a-2
3 a-3
4 b
5 b-2
6 c
In SQL Server I would use:
;with cte as(select *, row_number() over(partition by name order by id) rn from table)
update cte set name = name + '-' + cast(rn as varchar(10))
where rn <> 1
I am not strong in MySQL nonstandard queries.
Can I do something like this in MySQL?
You can do this:
UPDATE YourTable p
JOIN(SELECT t.id,t.name,count(*) as rnk
FROM YourTable t
INNER JOIN YourTable s on(t.name = s.name and t.id <= s.id)
GROUP BY t.id,t.name) f
ON(p.id = f.id)
SET p.name = concat(p.name,'-',f.rnk)
WHERE rnk > 1
This will basically use join and count to get the same as ROW_NUMBER() , and update only those who have more then 1 result(meaning the second,third ETC excluding the first)
In MySQL you can use variables in order to simulate ROW_NUMBER window function:
SELECT id, CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
FROM (
SELECT id, name,
#rn := IF(name = #n, #rn + 1,
IF(#n := name, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #n := '') AS vars
ORDER BY name, id) AS t
To UPDATE you can use:
UPDATE mytable AS t1
SET name = (
SELECT CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
FROM (
SELECT id, name,
#rn := IF(name = #n, #rn + 1,
IF(#n := name, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #n := '') AS vars
ORDER BY name, id) AS t2
WHERE t1.id = t2.id)
Demo here
You can also use UPDATE with JOIN syntax:
UPDATE mytable AS t1
JOIN (
SELECT id, rn, CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
FROM (
SELECT id, name,
#rn := IF(name = #n, #rn + 1,
IF(#n := name, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #n := '') AS vars
ORDER BY name, id) AS x
) AS t2 ON t2.rn <> 1 AND t1.id = t2.id
SET t1.name = t2.name;
The latter is probably faster than the former because it performs less UPDATE operations.
The next query will do it with less effort for the database:
UPDATE
tab AS tu
INNER JOIN
-- result set containing only duplicate rows that must to be updated
(
SELECT
t.id,
COUNT(*) AS cnt
FROM
tab AS t
-- join the same table by smaller id and equal value. That way you will exclude rows that are not duplicated
INNER JOIN
tab AS tp
ON
tp.name = t.name
AND
tp.id < t.id
GROUP BY
t.id
) AS tc
ON
tu.id = tc.id
SET
tu.name = CONCAT(tu.name, '-', tc.cnt + 1)

MySQL - Parallel merge two unrelated queries with same # of rows

I have two tables:
exam_outline_items:
jml_quiz_pool:
Of all the things I've tried, this got me the closest:
select t1.sequence, t1.title, t2.q_cat, t2.q_count
from student_pl.exam_outline_items t1
cross join pe_joomla.jml_quiz_pool t2
where t1.exam_outline_id = 5 and t1.chapter_num > 0
and t2.q_id = 1109 and t2.q_count > 0
group by title
Which produces this result:
I just need those q_cat values to be different, like they are in the 2nd query.
Thanks in advance for your help.
You have to have something to connect them with. If you don't have such a column, you can simulate one by creating a rownumber with variables.
select sequence, title, q_cat, q_count from (
select t1.sequence, t1.title, #r1 := #r1 + 1 as rownumber
from student_pl.exam_outline_items t1
, (select #r1 := 0) var_init
where t1.exam_outline_id = 5 and t1.chapter_num > 0
order by t1.sequence
) a
inner join
(
select t2.q_cat, t2.q_count, #r2 := #r2 + 1 as rownumber
from pe_joomla.jml_quiz_pool t2
, (select #r2 := 0) var_init
where t2.q_id = 1109 and t2.q_count > 0
order by t2.q_cat
) b on a.rownumber = b.rownumber;
Also note, that I used order by in those queries. In a database you have no sort order unless you explicitly set it with order by.

Times between laps

I have this SQL work fine except 1 point (at third value I need take previous time), this is the table (times) schema, output, and SQL:
id number time
-----------------------------------
1 9 00:00:10.000000
2 10 00:00:15.000000
3 9 00:00:22.000000
4 10 00:00:35.000000
1 9 00:00:55.000000
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.id) times
FROM (
SELECT t1.id, t1.number,
TIME_TO_SEC(t1.time) - COALESCE(SUM(TIME_TO_SEC(t2.time)), 0) time
FROM times t1
LEFT JOIN times t2 ON t1.number = t2.number AND t2.id < t1.id
GROUP BY t1.id, t1.number, t1.time
) t
GROUP BY number
Result this output:
number laps times
-----------------------------------
9 3 00:00:10,00:00:12,00:00:23
10 2 00:00:15,00:00:20
I need this REsult expected:
number laps times
-----------------------------------
9 3 00:00:10,00:00:12,00:00:33
10 2 00:00:15,00:00:20
Where third time - previos calculated time, in this case (00:00:10,00:00:12,00:00:23 - 00:00:12) I Tried but SUM all times previous.
SQL Fiddle
Only need change SUM to MAX
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.id) times
FROM (
SELECT t1.id, t1.number,
TIME_TO_SEC(t1.time) - COALESCE(MAX(TIME_TO_SEC(t2.time)), 0) time
FROM times t1
LEFT JOIN times t2 ON t1.number = t2.number AND t2.time < t1.time -- Suggest you don't use id to compare
GROUP BY t1.id, t1.number, t1.time
) t
GROUP BY number
See SQL FIDDLE DEMO
And here's another version without inner group by. They have the same effect, but different query plan. So you have choices to compare the performances.
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.time) times
FROM (
SELECT t1.id, t1.number, t1.time tm1,
(SELECT TIME_TO_SEC(t1.time) - COALESCE(MAX(TIME_TO_SEC(t2.time)),0)
FROM times t2
WHERE t1.number = t2.number AND t2.time < t1.time) AS time
FROM times t1
) t
GROUP BY number;
This was a bit tricky, but here is a solution:
The main problem is the "smaller than" condition in your join. So first you have to add a running number (rank) to your Groups based on this answer. Then you can join by that rank. The resulting query is a bit large but gives you the desired result:
SELECT t.number, COUNT(1) laps,
GROUP_CONCAT(SEC_TO_TIME(time) ORDER BY t.id) times
FROM (
SELECT t1.id, t1.number,
TIME_TO_SEC(t1.time) - COALESCE(SUM(TIME_TO_SEC(t2.time)), 0) time
FROM
(SELECT
number, id, time,
(
CASE number
WHEN #curType
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curType := number END
) AS rank
FROM times, (SELECT #curRow := 0, #curType := '') r
ORDER BY number, id
) as t1
LEFT JOIN
(SELECT
number, id, time,
(
CASE number
WHEN #curType
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curType := number END
) AS rank
FROM times, (SELECT #curRow := 0, #curType := '') r
ORDER BY number, id
) t2
ON t1.number = t2.number and t2.rank+1 = t1.rank
GROUP BY t1.id, t1.number, t1.time
) t
GROUP BY number
gives:
| NUMBER | LAPS | TIMES |
|--------|------|----------------------------|
| 9 | 3 | 00:00:10,00:00:12,00:00:33 |
| 10 | 2 | 00:00:15,00:00:20 |