2016-08-23 38 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个记录每个ID,并记录应随机挑选出的是独特的每个ID 。举例 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

,因为我需要运行病例对照研究来查找控制人员,这些人员是在医院中与患有疾病的病例相匹配的患者,并给出一些匹配条件。在我给出了一些条件之后,我有一个类似于上面的表格,但有一个控件匹配超过1个案例 – emisu

+0

因此,考虑到上面的数据集,可用结果的范围实际上非常有限?这基本上是人类数独。而follow_up基本上与这个问题无关,对吧? – Strawberry

回答

0

这是一个非常好,非常具有挑战性的SQL问题。

你有一个非常具有挑战性的一系列要求: 1.无matched_id应该不止一次出现在结果集中 2.没有ID被赋予两个以上的比赛 3.匹配是随机的

我们将坚持纯粹的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 

如果有每个ID足够matched_id值,这可能足以满足您的使用案例。但是,如果存在隐藏的第四个要求会怎么样: 4.如果可能,一个ID应该收到一个匹配。

换句话说,如果随机洗牌的结果,matched_id被分配到的ID有几个其他比赛中,但进一步下跌的结果集,这是唯一的比赛一个ID是什么?每个ID都与matched_id匹配的最佳解决方案是可能的,但它从未发生过,因为所有matched_ids在流程的早期用完了?

例如:

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和它们的匹配被随机分配的,如果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 matched_id每个ID,但如果我随机分配最多2个matched_id每个ID,有一些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

嗨,预期的输出是一个包含三列的表:distinct matched_id; follow_up; id(id在每一行中重复两次,例如id “1”表示匹配标识“1​​9”,标识“1”表示匹配标识“20”,标识“2”表示匹配标识“22”,标识“2”表示匹配标识“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