MySQL Случайные уникальные данные о проценте из таблицы с 1 столбцом (без первичного ключа) - PullRequest
1 голос
/ 02 августа 2020

Мне нужно взять данные из таблицы (100% всех данных) и разбить их на 3 столбца.

Пример: у нас есть данные:

numbers
80174
91467
1105
12040
62224
46508
33149
61384
10811
84923

Нам нужно взять:

      | Random 60% of all | Random  40% of all
      | unique and not    | unique and not 
  All | contained in 40%  | contained in 60% 
      | of the column     | of the column
----------------------------------------------
80174 |      84923        |      33149
91467 |      91467        |      61384
1105  |       1105        |      10811
12040 |      62224        |      80174
62224 |      12040        |     
46508 |      46508        |     
33149 |                   |
61384 |                   |
10811 |                   |
84923 |                   |

Ответы [ 3 ]

2 голосов
/ 02 августа 2020

Здесь используется функция ORDER BY RAND () для рандомизации строк и используется оконная функция mysql 8 ROW_NUMBER, чтобы сначала разделить число на 60/40, а затем объединить их.

вы можете перестроить функцию row_number в mysql 5.x, но это не так красиво

Отредактируйте после совета torpas, подсчитайте количество необходимых строк. Изменить 2: после еще одного комментария от forpas я заменил CEIL на RAND

Я думаю, что должно быть усовершенствованное решение, в котором больше используется MODULO

CREATE TABLE Table1
    (`numbers` int)
;
    
INSERT INTO Table1
    (`numbers`)
VALUES
    (80174),
    (91467),
    (1105),
    (12040),
    (62224),
    (46508),
    (33149),
    (61384),
    (10811),
    (84923),
    (80179),
    (91469),
    (1109),
    (12049),
    (62229)    
;
WITH rand_num as (SELECT `numbers`, ROW_NUMBER() OVER (ORDER BY RAND()) as rn FROM Table1 ),
limitscal as (SELECT ROUND((COUNT(*)  * 6 / 10),0)  si_x  FROM Table1),
countcal as (SELECT COUNT(*)  cnt  FROM Table1),
60_num as (SELECT `numbers`, ROW_NUMBER() OVER (ORDER BY RAND()) as rn2 
            FROM  rand_num CROSS JOIN limitscal CROSS JOIN countcal
            WHERE rn MOD countcal.cnt < limitscal.si_x)
,40_num as (SELECT `numbers`, ROW_NUMBER() OVER (ORDER BY RAND()) as rn2 
            FROM  rand_num CROSS JOIN limitscal  CROSS JOIN countcal 
            WHERE rn MOD countcal.cnt >= limitscal.si_x )
SELECT 6_n.`numbers`,4_n.`numbers` FROM 60_num 6_n LEFT JOIN 40_num 4_n ON 6_n.rn2 = 4_n.rn2
numbers | numbers
------: | ------:
  10811 |   61384
  80174 |   12049
  12040 |   46508
  91467 |   84923
  80179 |    1109
  91469 |   62224
  33149 |    <em>null</em>
   1105 |    <em>null</em>
  62229 |    <em>null</em>

db <> fiddle здесь

0 голосов
/ 02 августа 2020

Вы хотите пронумеровать ваши строки случайным образом. И вы хотите сделать это дважды. Один раз для случайного разделения на 60% и 40%, один раз для случайного упорядочения всех столбцов.

Начиная с MySQL 8, вы можете использовать оконную функцию ROW_NUMBER для этого. Однако применение этого дважды как row_number() over (order by rand()) приводит к тому же дважды случайному порядку, потому что MySQL видит, что вы упорядочиваете по одному и тому же выражению. Итак, немного измените выражение, например, добавив две разные константы.

Остальное - это два внешних соединения со 100% строками, одно для соединения с 60%, одно для соединения с оставшимися 40%.

with prepared as
(
  select
    number,
    row_number() over (order by rand() + 0) as rn1,
    row_number() over (order by rand() + 1) as rn2,
    count(*) over () as cnt
  from numbers
)
, p100 as (select rn1 as rn, number from prepared)
, p60 as (select rn2 as rn, number from prepared where rn2 / cnt <= 0.6)
, p40 as (select cnt - rn2 + 1 as rn, number from prepared where rn2 / cnt > 0.6)
select
  p100.number as number1,
  p60.number as number2,
  p40.number as number3
from p100
left join p60 on p60.rn = p100.rn
left join p40 on p40.rn = p100.rn
order by p100.rn;

Демо: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b14419fd15f8a7987c10f2ef25ced826

0 голосов
/ 02 августа 2020

Вы можете назначить группировку на основе rand():

select t.*, (case then rand() < 0.6 then 1 else 2 end) as grouping
from t;

Обратите внимание, что это приблизительно 60% / 40%. Если вам нужно точное разделение, вы можете использовать оконные функции:

select t.*,
       (case when seqnum <= 0.6 * cnt then 1 else 2 end) as grouping
from (select t.*, count(*) over () as cnt, row_number() over (order by rand()) as seqnum
      from t
     ) t
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...