2016-08-23 2 views
0

У меня есть таблица А, как показано нижеИспользовать SQL для случайного выбора эксклюзивных записей

id (integer) 
follow_up (integer, days under observation) 
matched_id (integer) 


id ; follow_up ; matched_id 
1 ; 10    ; 19 
1 ; 10    ; 20 
1 ; 10    ; 21 
2 ; 5    ; 22 
2 ; 5    ; 23 
2 ; 5    ; 24 
2 ; 5    ; 19 
2 ; 5    ; 20 
3 ; 6    ; 25 
3 ; 6    ; 26 
3 ; 6    ; 27 
4 ; 7    ; 19 
4 ; 7    ; 28 
4 ; 7    ; 29 

Я хотел бы ограничить до 2 записей в ид, а записи должны быть случайным образом подобраны и быть эксклюзивными для каждого идентификатора , Например, matched_id: «19» и «20» были присвоены id: 1, тогда «19» и «20» не должны присваиваться id: 2 matched_id: «19» было присвоено id: 1, затем «19» не следует указывать на id: 4 и т. Д. Для остальной части таблицы.

требуют вывод

id ; follow_up ; matched_id 
1 ; 10    ; 19 
1 ; 10    ; 20 
2 ; 5    ; 22 
2 ; 5    ; 23 
3 ; 6    ; 25 
3 ; 6    ; 26 
4 ; 7    ; 28 
4 ; 7    ; 29 

Пожалуйста, помогите мне. Спасибо огромное!

+0

Зачем вам нужен этот результат? –

+0

, потому что мне нужно запустить исследование контроля над заболеванием, чтобы найти элементы управления, которые являются пациентами в больнице, которые соответствуют случаям с болезнью, при определенных условиях. После того, как я дал некоторые условия, у меня есть таблица, аналогичная приведенной выше, но есть элементы управления, которые соответствуют более чем одному случаю – emisu

+0

Итак, учитывая приведенные выше данные, диапазон допустимых результатов на самом деле очень ограничен? Это в основном человеческая судоку. И follow_up в основном не имеет отношения к этой проблеме, не так ли? – Strawberry

ответ

0

Это очень хороший и очень сложный вопрос SQL.

У вас есть очень сложный набор требований: 1. Ни matched_id не должно появляться более одного раза в результате установки 2. Нет ID не будет дано более двух матчей 3. Совпадение случайным

We будет придерживаться чистого SQL-решения, предполагая, что вы не можете вернуть, скажем, больший набор результатов и сделать некоторую фильтрацию с использованием бизнес-логики на вашем языке реализации.

Во-первых, давайте рассмотрим случайное задание. Случайный порядок элементов внутри групп - забавный вопрос. Я решил заняться этим, заказав хэш SHA1 данных в строке (id, follow_up, matched_id), что даст повторяющийся результат с чувством случайности. (Это было бы лучше, если бы столбец, который содержал дату/время создания или изменения.)

SELECT * FROM 
(
    SELECT 
    a.id, 
    a.follow_up, 
    a.matched_id, 
    a.rank_hash, 
    count(*) rank 
    FROM 
    (SELECT *, SHA1(CONCAT(id, follow_up, matched_id)) rank_hash FROM TableA) a 
    JOIN 
    (SELECT *, SHA1(CONCAT(id, follow_up, matched_id)) rank_hash FROM TableA) b 
    ON a.rank_hash >= b.rank_hash 
    AND a.id = b.id 
    GROUP BY a.id, a.matched_id 
    ORDER BY a.id, rank 
) groups 
WHERE rank <= 2 
GROUP BY matched_id 

Это может быть достаточно для случая использования, если имеются достаточные значения matched_id для каждого идентификатора. Но что, если есть скрытое четвертое требование: 4. Если возможно, идентификатор должен получить соответствие.

Другими словами, что, если в результате случайного перетасовки соответствие идентификатора сопоставлено идентификатору, имеющему несколько других совпадений, но дальше по результату результат был равен для идентификатора? Было возможно оптимальное решение, в котором каждый идентификатор был сопоставлен с matched_id, но этого не произошло, потому что все сопоставленные_иды были использованы ранее в этом процессе?

Например:

CREATE TABLE TableA 
    (`id` int, `follow_up` int, `matched_id` varchar(1)) 
; 

INSERT INTO TableA 
    (`id`, `follow_up`, `matched_id`) 
VALUES 
    (1, 10, 'A'), 
    (1, 10, 'B'), 
    (1, 10, 'C'), 
    (2, 5, 'D'), 
    (2, 5, 'E'), 
    (2, 5, 'F'), 
    (3, 5, 'C') 
; 

В приведенном выше наборе, если идентификаторы и их матчи назначаются случайным образом, если ID 1 получает назначение matched_id C, то ID 3 не получит matched_id вообще.

Что делать, если мы сначала узнаем, сколько совпадений получено ID, и порядок от первого?

SELECT 
    a.*, 
    frequency 
FROM TableA a 
JOIN 
(SELECT 
    matched_id, 
    count(*) frequency 
    FROM 
    TableA 
    GROUP BY matched_id 
) b 
ON a.matched_id = b.matched_id 
GROUP BY a.matched_id 
ORDER BY b.frequency 

Здесь может быть полезен язык программирования посредника, который поможет ограничить набор результатов.

Но обратите внимание, что мы также потеряли наше требование случайности! Как вы можете видеть, чистое решение SQL может стать довольно уродливым. Это действительно возможно, объединяя методы, описанные выше.

Надеюсь, это вызовет ваше воображение.

+0

Вы правы, что частота дала мне от 54-300 соответствий по id, но если я случайным образом распределяю до max 2 matched_id на один идентификатор, есть некоторая идентификация, у которой осталось только 1 matched_id. Это ужасно, и я не думал об этом раньше :(но это хорошее предложение. – emisu

0

Наряду с RAND() и MySQL user defined variables вы можете достичь этого:

SELECT 
t.id, 
t.follow_up, 
t.matched_id 
FROM 
(
    SELECT 
    randomTable.*, 
    IF(@sameID = id, @rn := @rn + 1, 
     IF(@sameID := id, @rn := 1, @rn := 1) 
    ) AS rowNumber 
    FROM 
    (
     SELECT 
     * 
     FROM tableA 
     ORDER BY id, RAND() 
    ) AS randomTable 
    CROSS JOIN (SELECT @sameID := 0, @rn := 0) var 
) AS t 
WHERE t.rowNumber <= 2 
ORDER BY t.id 

See Demo

+0

Спасибо за ваш ответ. Я запустил mysql, однако есть еще некоторые matched_id, которые не являются эксклюзивными, то есть принадлежат больше, чем один id. – emisu

+0

Итак, каков ожидаемый результат в этом случае? Я что-то упустил? – 1000111

+0

привет, ожидаемый вывод - это таблица с тремя столбцами: different matched_id; follow_up; id (id повторяется дважды в каждой строке, например id «1» для matched_id «19», id «1» для matched_id «20», id «2» для matched_id «22», id «2» для matched_id «23», – emisu

0

Вот решение для конкретной задачи данной. Он не масштабируется!

SELECT * 
    FROM 
    (SELECT a.matched_id m1 
      , b.matched_id m2 
      , c.matched_id m3 
      , d.matched_id m4 
     FROM my_table a 
     JOIN my_table b 
      ON b.matched_id NOT IN(a.matched_id) 
     JOIN my_table c 
      ON c.matched_id NOT IN(a.matched_id,b.matched_id) 
     JOIN my_table d 
      ON d.matched_id NOT IN(a.matched_id,b.matched_id,c.matched_id) 
     WHERE a.id = 1 
      AND b.id = 2 
      AND c.id = 3 
      AND d.id = 4 
    ) x 
    JOIN 
    (SELECT a.matched_id n1 
      , b.matched_id n2 
      , c.matched_id n3 
      , d.matched_id n4 
     FROM my_table a 
     JOIN my_table b 
      ON b.matched_id NOT IN(a.matched_id) 
     JOIN my_table c 
      ON c.matched_id NOT IN(a.matched_id,b.matched_id) 
     JOIN my_table d 
      ON d.matched_id NOT IN(a.matched_id,b.matched_id,c.matched_id) 
     WHERE a.id = 1 
      AND b.id = 2 
      AND c.id = 3 
      AND d.id = 4 
    ) y 
    ON y.n1 NOT IN(x.m1,x.m2,x.m3,x.m4) 
    AND y.n2 NOT IN(x.m1,x.m2,x.m3,x.m4) 
    AND y.n3 NOT IN(x.m1,x.m2,x.m3,x.m4) 
    AND y.n4 NOT IN(x.m1,x.m2,x.m3,x.m4) 
ORDER 
    BY RAND() LIMIT 1; 

+----+----+----+----+----+----+----+----+ 
| m1 | m2 | m3 | m4 | n1 | n2 | n3 | n4 | 
+----+----+----+----+----+----+----+----+ 
| 20 | 24 | 27 | 29 | 21 | 23 | 26 | 28 | 
+----+----+----+----+----+----+----+----+ 

Таким образом, в этом примере пары:

id1: 20,21 
id2: 24,23 
id3: 27,26 
id4: 29,28 
+0

Благодарим вас за предложение. Может ли быть обобщено для таблицы с 530 id, а не только 4 id, как в моем примере? – emisu

+0

Возможно, вы пропустили первую строку моего ответа! – Strawberry

Смежные вопросы