2017-02-12 29 views
2

我正在處理醫療數據,我想找到給某位患者的主要診斷信息。如何選擇在PostgreSQL中的值列表中匹配的字符串的第一部分?

診斷全部在一列中作爲逗號分隔的字符串。例如「A10.3,B55.2,A10.1」。可以將此表稱爲患者並且診斷列爲diag_list。我想要創建一個新列,其中包含來自diag_list的第一部分,該列在不同表格中具有匹配,其中包含可能被視爲「主要」的診斷列表。我們可以將此表稱爲ICD10,並將主診斷列表中的列標記爲primary_diag

我想要第一場比賽的原因是diag_list已經根據症狀的嚴重程度排序。所以我試圖找出最嚴重的症狀,也可以認爲是主要的診斷。

我試着先將diag_list轉換爲一個數組,string_to_array,但找不到有條件地從這個新數組中選擇第一個匹配的方法。你會如何做這個選擇?還是有一些完全不同的方法可以得出相同的結論:簡單和/或更高效?

+1

請添加一些數據樣本,包括請求的結果 –

回答

2

一旦你有你的string_to_array你需要unnest它。然後,您需要加入ICD10,並對每位患者採取first診斷。

假設我們有以下數據:

CREATE TABLE patients 
(
    patient_id integer PRIMARY KEY, 
    diag_list text NOT NULL 
) ; 

INSERT INTO patients 
VALUES 
    (1, 'A10.3,B55.2, A10.1') , 
    (2, 'A10.3, A10.1, C20.2') ; 

CREATE TABLE ICD10 
(
    primary_diag text PRIMARY KEY, 
    diagnose text 
) ; 

INSERT INTO ICD10 
VALUES 
    ('B55.2', 'Something Bad'), 
    ('A10.1', 'Somehitng Worse'); 

有了這些數據,我們就可以開始unnest with ordinality數據:

SELECT 
    patient_id, trim(diag) AS diag, nr 
FROM 
    patients 
    JOIN LATERAL unnest(string_to_array(diag_list, ',')) 
     WITH ORDINALITY AS a(diag, nr) ON true ; 

,並得到

+------------+-------+----+ 
| patient_id | diag | nr | 
+------------+-------+----+ 
|   1 | A10.3 | 1 | 
|   1 | B55.2 | 2 | 
|   1 | A10.1 | 3 | 
|   2 | A10.3 | 1 | 
|   2 | A10.1 | 2 | 
|   2 | C20.2 | 3 | 
+------------+-------+----+ 

下一步:加入這個數據與ICD10

WITH patients_and_diags AS 
(
SELECT 
    patient_id, trim(diag) AS diag, nr 
FROM 
    patients 
    JOIN LATERAL unnest(string_to_array(diag_list, ',')) 
     WITH ORDINALITY AS a(diag, nr) ON true 
) 
SELECT 
    patient_id, diag, nr, diagnose 
FROM 
    patients_and_diags 
    JOIN ICD10 ON ICD10.primary_diag = patients_and_diags.diag ; 

...並獲得:

+------------+-------+----+-----------------+ 
| patient_id | diag | nr | diagnose  | 
+------------+-------+----+-----------------+ 
|   1 | B55.2 | 2 | Something Bad | 
|   1 | A10.1 | 3 | Somehitng Worse | 
|   2 | A10.1 | 2 | Somehitng Worse | 
+------------+-------+----+-----------------+ 

現在,我們需要採取只有最小的 'NR' 每個patient_id

下面的查詢,盡一切一步

WITH patients_and_diags AS 
(
SELECT 
    patient_id, trim(diag) AS diag, nr 
FROM 
    patients 
    JOIN LATERAL unnest(string_to_array(diag_list, ',')) 
     WITH ORDINALITY AS a(diag, nr) ON true 
) 
, patients_and_ICD10 AS 
(
SELECT 
    patient_id, diag, nr, diagnose 
FROM 
    patients_and_diags 
    JOIN ICD10 ON ICD10.primary_diag = patients_and_diags.diag 
) 
, first_ICD10 AS 
(
SELECT 
    patient_id, min(nr) AS nr 
FROM 
    patients_and_ICD10 
GROUP BY 
    patient_id 
) 
SELECT 
    patient_id, diag, diagnose 
FROM 
    first_ICD10 
    JOIN patients_and_ICD10 USING(patient_id, nr) ; 

...給你拿:

+------------+-------+-----------------+ 
| patient_id | diag | diagnose  | 
+------------+-------+-----------------+ 
|   1 | B55.2 | Something Bad | 
|   2 | A10.1 | Somehitng Worse | 
+------------+-------+-----------------+ 

您可以在http://rextester.com/RDYPD23700

檢查所有的查詢可以由通過使用一些WINDOW功能短;但我認爲這種循序漸進的方法更爲明確。

+0

不錯的答案,我認爲'min(nr)'最好用窗口函數完成。不知道rextester.com,看起來比sqlfiddle快得多! – Andomar

+0

@Andomar:是的,可能只需要'first_value() ''窗口'按病人分組'和'按nr排序'但我不確定一次優化(以及太多概念)永遠是最好的主意,我們不要[「過早優化」](http: //wiki.c2.com/?PrematureOptimization);-) – joanolo

+0

謝謝!這解決了我的問題,你的解釋很容易理解,我不知道'w ith ordinality'關鍵字,它可能會在我現在處理的數據在多種情況下派上用場。 –

1

這很棘手。從Postgres 9.4開始,unnest()的關鍵字爲with ordinality。這包括一個職位欄。這與一些其他的東西相結合,將你需要的東西:

select distinct on (p.patientid) p.*, d.* 
from patients p, later 
    unnest(string_to_array(p.diag_list, ',')) with ordinality dp(code, ord) join 
    diagnoses d 
    on d.code = dp.code 
order by p.patientid, ord asc; 

不出意外,歐文Brandstetter修改有這個問題的discussion以及如何解決它。

+0

不幸的是,「第一」是有條件的:「diag_list中的第一部分在不同的表中匹配」 – Andomar

+0

@Andomar。。。謝謝。這使得問題更加棘手。 –

0

您可以使用unnest"normalize"進行診斷。這意味着每個患者 - 診斷組合都有一行。使用generate_subscripts將該診斷的位置添加到列表中。 (Postgres的9.4及更高版本,with ordinality是更好,因爲在其他答案建議。)你可以使用位置訂購diagnistics,並在列表中篩選出最高的主要診斷:

with normal_pat as 
     (
     select name 
     ,  unnest(string_to_array(diag_list, ',')) as diag 
     ,  generate_subscripts(string_to_array(diag_list, ','),1) as pos 
     from patients 
     ) 
,  numbered_pat as 
     (
     select row_number() over (partition by name order by pos) rn 
     ,  * 
     from normal_pat 
     join diagnostics d 
     on  normal_pat.diag = d.primary_diag 
     ) 
select name 
,  diag 
,  pos as position_of_diagnostic_in_list 
from numbered_pat 
where rn = 1 

這裏是一個工作示例爲SQL Fiddlerextester

相關問題