如何在PostgreSQL的值列表中选择具有匹配项的字符串的第一部分?

如何在PostgreSQL的值列表中选择具有匹配项的字符串的第一部分?,sql,postgresql,string-matching,Sql,Postgresql,String Matching,我正在处理医疗数据,我想找到特定患者的主要诊断 诊断都以逗号分隔的字符串形式列在一列中。例如“A10.3、B55.2、A10.1”。让我们调用此表患者和包含诊断列表的列诊断列表。我想创建一个新列,其中包含来自诊断列表的第一部分,该部分在不同的表中有一个匹配项,该表包含可被视为“主要”的诊断列表。让我们调用此表ICD10,并将主要诊断列表列为primary\u diag 我想要第一个匹配的原因是,diag_list已经根据症状的严重程度进行了排序。所以我试图找到最严重症状的诊断,也可以被认为是主要

我正在处理医疗数据,我想找到特定患者的主要诊断

诊断都以逗号分隔的字符串形式列在一列中。例如“A10.3、B55.2、A10.1”。让我们调用此表患者和包含诊断列表的列诊断列表。我想创建一个新列,其中包含来自诊断列表的第一部分,该部分在不同的表中有一个匹配项,该表包含可被视为“主要”的诊断列表。让我们调用此表ICD10,并将主要诊断列表列为primary\u diag

我想要第一个匹配的原因是,diag_list已经根据症状的严重程度进行了排序。所以我试图找到最严重症状的诊断,也可以被认为是主要诊断


我试图首先将diag_列表转换为一个数组,该数组使用string_to_array,但找不到有条件地从这个新数组中选择第一个匹配项的方法。你会怎么做呢?还是有完全不同的方法可以得出更简单和/或更有效的相同结论?

这很棘手。从Postgres 9.4开始,您就有了
unnest()
和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;

毫不奇怪,Erwin Brandstetter对这个问题以及如何解决这个问题有着广泛的了解。

一旦你有了
字符串到数组
,你就需要
取消它。然后,您需要加入
ICD10
,对于每个患者,首先进行
诊断

假设我们有以下数据:

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');
有了这些数据,我们可以开始查看您的数据:

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 |
+------------+-------+----+-----------------+
现在,我们只需要为每个患者id提取最小的“nr”

下面的查询一步完成所有操作

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 |
+------------+-------+-----------------+
你可以随时查看一切


使用一些
窗口
函数可以缩短查询时间;但我认为这种循序渐进的方法更为明确

您可以使用
unest
进行诊断。这意味着每个患者有一行诊断组合。使用
generate_subscripts
在列表中添加该诊断的位置。(对于Postgres 9.4及更高版本,
具有有序性更好,如其他答案中所示。)您可以使用该位置对诊断进行排序,并筛选列表中最高的主要诊断:

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

以下是or的一个工作示例。

不幸的是,“第一个”是有条件的:“诊断列表中在不同表中有匹配项的第一部分”@Andomar…谢谢。这使问题变得更加棘手。请添加一些数据样本,包括请求的结果和答案。我认为
min(nr)
最好使用窗口函数。我不知道rextester.com,看起来比sqlfiddle快得多!@Andomar:是的,可能只是获取窗口的
第一个值()
,按患者分组
和按nr排序
。但我不确定是否进行了所有优化(以及太多的概念)立刻是最好的主意。我们不要;-)谢谢!这解决了我的问题,你的解释也很容易理解。我不知道带序数的
关键字,它可能会在我现在处理的数据的多种情况下派上用场。