2016-08-23 94 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