2

Question Mysql Random Row Query on Inner Join is much the same as mine but it was never answered.

I have a master table m and slave s. S contains 1 to many rows for each m. I would like a query that selects every master row joined to exactly one randomly chosen slave.

If the table schemas were:

M
---
id

S
---
id
mid

then, in pseudo code the query would be: select * from m inner join s on m.id = s.mid where s.id is one randomly chosen from the values that exist

Can this be translated into real SQL?

6
  • 1
    What us your MySQL server version ? Commented Nov 28, 2018 at 3:13
  • Is S.id, an autoincrement / INT value? Can it be assumed to be uniform without significant gaps (minor lack of equal chance in random selection)? Commented Nov 28, 2018 at 3:37
  • Another bunch of answers Commented Nov 28, 2018 at 3:40
  • Actually, i see my host is running MariaDB. 10.1.31-MariaDB-cll-lve Innodb_version 5.6.36-83.0 Commented Nov 28, 2018 at 4:44
  • s.id will be autoincrement but there may well be gaps caused by deletions Commented Nov 28, 2018 at 4:46

2 Answers 2

2

I think the following query does the required job but using a subquery (not inner join):

SELECT *, (SELECT id FROM S WHERE S.mid = M.id ORDER BY RAND() LIMIT 1) AS S_id
FROM M

Here is a link to test it. Hope it helps.

Sign up to request clarification or add additional context in comments.

1 Comment

This isn't efficient for a large S table: jan.kneschke.de/projects/mysql/order-by-rand
0

This can be solved using Row_Number() concept. We need to randomly assign row number values within a partition of mid in the table s. And, do a Join from the m table to s using mid and row_number = 1. This will pick a single Random row everytime.

In MySQL version below 8, we can use User-defined Variables to emulate Row_Number(). To understand how this works, you may check this answer for the explanation: https://stackoverflow.com/a/53465139/2469308

Note that this technique will be efficient on Large tables than using a Subquery (in the SELECT clause), as it will be doing overall table Sorting only once

View on DB Fiddle

create table m (id int, m_nm varchar(10));
create table s (id int, 
                mid int references m(mid), 
                s_nm varchar(10));

insert into m values(1, "a");
insert into m values(2, "b");
insert into m values(3, "c");

insert into s values(1, 1, "aa");
insert into s values(2, 1, "aa");
insert into s values(3, 2, "bb");
insert into s values(4, 2, "bbb");
insert into s values(5, 2, "bbbb");
insert into s values(6, 3, "cc");
insert into s values(7, 3, "ccc");

Query

SELECT 
  m.*, s_dt.id, s_dt.mid, s_dt.s_nm 
FROM 
  m 
JOIN 
(
SELECT
  @rn := IF(@m = dt.mid, @rn+1, 1) AS row_num,   
  @m := dt.mid AS mid, 
  dt.id, 
  dt.s_nm 
FROM 
( 
SELECT
   id, mid, s_nm, RAND() as rand_num
 FROM s
 ORDER BY mid, rand_num ) AS dt
CROSS JOIN (SELECT @rn:=0, @m:=0) AS user_vars 
) AS s_dt 
  ON s_dt.mid = m.id AND 
     s_dt.row_num = 1;

Result (Run #1)

| id  | m_nm | id  | mid | s_nm |
| --- | ---- | --- | --- | ---- |
| 1   | a    | 2   | 1   | aa   |
| 2   | b    | 5   | 2   | bbbb |
| 3   | c    | 7   | 3   | ccc  |

Result (Run #2)

| id  | m_nm | id  | mid | s_nm |
| --- | ---- | --- | --- | ---- |
| 1   | a    | 1   | 1   | aa   |
| 2   | b    | 4   | 2   | bbb  |
| 3   | c    | 6   | 3   | cc   |

Result (Run #3)

| id  | m_nm | id  | mid | s_nm |
| --- | ---- | --- | --- | ---- |
| 1   | a    | 1   | 1   | aa   |
| 2   | b    | 3   | 2   | bb   |
| 3   | c    | 7   | 3   | ccc  |

MySQL 8.0.2+ / MariaDB 10.3+ solution would be simply the following:

SELECT 
  m.*, s_dt.id, s_dt.mid, s_dt.s_nm 
FROM 
  m 
JOIN 
(
  SELECT
    s.*, 
    ROW_NUMBER() OVER w AS row_num
  FROM s
  WINDOW w AS (PARTITION BY mid 
               ORDER BY RAND())
) AS s_dt 
  ON s_dt.mid = m.id AND 
     s_dt.row_num = 1

View on DB Fiddle

10 Comments

Yeah, I just created my own. Here it is: sqlfiddle.com/#!9/1624c6/1
Same result I guess.
No, all ids are numeric. Check the link sqlfiddle.com/#!9/1624c6/2
No worries. Have fun!
@TeeKea do check the updated answer. There may have been a typo in previous answer; as the concept in the new query still remains the same.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.