好吧,不太清楚您要查找的内容,但您确实表明您希望根据最多三种药品的组合执行某种频率分析。
此类分析的第一步是获取药房数据,并为每个 user_id 确定他们参与的 1、2 和 3 个 drug_dose 组合的集合,因为您可能想要这样做对substance_name、drug_name 和/或drug_code 进行相同的分析,我将把厨房水槽扔给它,然后全部四个。不知道您在后端拥有哪种类型的数据库,我将在此示例中使用 SQL Server 2017,尽管使用的概念适用于 Oracle、MySQL、PostgreSQL 等数据库,尽管语法可能有所不同。
要创建drug_code 和其他组合,我将首先将pharmacy_data 表连接到drug_reference 表,然后对复合数据使用递归查询:
with usage_info as (
select pd.user_id
, dr.drug_code
, dr.drug_name
, dr.substance_name
, concat(dr.substance_name,dr.dosage,dr.unit) drug_dose
from pharmacy_data pd
join drug_reference dr
on dr.drug_code = pd.drug_code
), recur(user_id, combo_id, dc_combo, dc_combo_size, dn_combo, sn_combo, dd_combo, last_dc) as (
-- Anchor part
select user_id
, cast(cast(drug_code as binary(4)) as varbinary(max))
, cast(drug_code as varchar(max))
, 1
, cast(drug_name as varchar(max))
, cast(substance_name as varchar(max))
, cast(drug_dose as varchar(max))
, drug_code
from usage_info
union all
-- Recursive Part
select prev.user_id
, prev.combo_id+cast(curr.drug_code as binary(4))
, prev.dc_combo+','+cast(curr.drug_code as varchar(max))
, prev.dc_combo_size+1
, prev.dn_combo+','+curr.drug_name
, prev.sn_combo+','+curr.substance_name
, prev.dd_combo+','+curr.drug_dose
, curr.drug_code
from recur prev
join usage_info curr
on prev.user_id = curr.user_id
and prev.last_dc < curr.drug_code
and prev.dc_combo_size < 3 -- Maximum combination size
)
从上述公用表表达式中选择您问题中提供的数据:
select * from recur;
显示dn_combo、sn_combo 和可能的dd_combo 列的分组中存在一些违规行为,例如,'CAZERTA,BEXERA' 和 'BEXERA,CAZERTA' 都存在dn_combos,这确实应该等价
为了纠正这个问题,我将通过拆分组合并按排序顺序重新组合来规范化组合。在此过程中,我还将对 user_id 可能具有两个或多个等效但不相同的产品的任何实例进行重复数据删除,例如两种不同剂量的同一种药物:
, combos as (
select user_id
, combo_id
, dc_combo
, dc_combo_size
, -- Normalize and deduplicate Drug_Name combos
(select string_agg(value,',') within group (order by value)
from (select distinct value from string_split(dn_combo,',')) dn
) dn_combo
, (select count(distinct value) from string_split(dn_combo,',')) dn_combo_size
, -- Normalize and deduplicate Substance_Name combos
(select string_agg(value,',') within group (order by value)
from (select distinct value from string_split(sn_combo,',')) sn
) sn_combo
, (select count(distinct value) from string_split(sn_combo,',')) sn_combo_size
, -- Normalize and deduplicate Drug_Dose combos
(select string_agg(value,',') within group (order by value)
from (select distinct value from string_split(dd_combo,',')) ddc
) dd_combo
, (select count(distinct value) from string_split(dd_combo,',')) dd_combo_size
from recur
)
现在,虽然您可以选择 count(user_id) over (partition by <grouping_column>) 来获取每种药物组合的出现频率,但这些数字可能会被夸大。例如,如果您的数据有额外的user_id 999 和drug_codes 50、100、200 和 350(这是 BEXERA 以及 AXIOM 和 CAZERTA 的两种不同剂量),那么user_id 999 将显示多个包括 BEXERA 在内的每个组合的时间。根据您的数据库风格,您可以只选择count(DISTINCT user_id) over (partition by <grouping_column>),但从 SQL Server 2017 开始,它不允许在分析函数中使用 distinct 运算符。 </shrug> 我们仍然可以这样做,只需再采取一步来识别每个组的唯一值。输入 Common Table combo2,我们在其中计算各个分区的行号:
, combo2 as (
select user_id
, combo_id
, dc_combo
, dc_combo_size
, row_number() over (partition by dc_combo, user_id order by dc_combo) dc_uid_rn
, dn_combo
, dn_combo_size
, row_number() over (partition by dn_combo, user_id order by dc_combo) dn_uid_rn
, row_number() over (partition by dn_combo, dc_combo order by user_id) dn_combo_rn
, sn_combo
, sn_combo_size
, row_number() over (partition by sn_combo, user_id order by dc_combo) sn_uid_rn
, row_number() over (partition by sn_combo, dc_combo order by user_id) sn_combo_rn
, dd_combo
, dd_combo_size
, row_number() over (partition by dd_combo, user_id order by dc_combo) dd_uid_rn
, row_number() over (partition by dd_combo, dc_combo order by user_id) dd_combo_rn
from combos
)
然后最后计算我们有两种类型的计数。 uid_cnt 列是每个组合的不同 user_ids 的计数,combo_cnt 列表示构成较小粒度分组的不同 drug_code 组合的数量:
select user_id
, combo_id
, dc_combo
, dc_combo_size
, count(case dc_uid_rn when 1 then 1 end) over (partition by dc_combo) dc_uid_cnt
, dn_combo
, dn_combo_size
, count(case dn_uid_rn when 1 then 1 end) over (partition by dn_combo) dn_uid_cnt
, count(case dn_combo_rn when 1 then 1 end) over (partition by dn_combo) dn_combo_cnt
, sn_combo
, sn_combo_size
, count(case sn_uid_rn when 1 then 1 end) over (partition by sn_combo) sn_uid_cnt
, count(case sn_combo_rn when 1 then 1 end) over (partition by sn_combo) sn_combo_cnt
, dd_combo
, dd_combo_size
, count(case dd_uid_rn when 1 then 1 end) over (partition by dd_combo) dd_uid_cnt
, count(case dd_combo_rn when 1 then 1 end) over (partition by dd_combo) dd_combo_cnt
from combo2
order by dn_combo, dd_combo
上面的代码连同我的其他示例数据导致以下 table。要查看它的实际效果,请查看SQL Fiddle:
| user_id | dc_combo | dc_combo_size | dc_uid_cnt | dn_combo | dn_combo_size | dn_uid_cnt | dn_combo_cnt | sn_combo | sn_combo_size | sn_uid_cnt | sn_combo_cnt | dd_combo | dd_combo_size | dd_uid_cnt | dd_combo_cnt |
|---------|-------------|---------------|------------|----------------------|---------------|------------|--------------|---------------------------------|---------------|------------|--------------|-------------------------------------------------|---------------|------------|--------------|
| 3 | 200 | 1 | 2 | AXIOM | 1 | 4 | 3 | nsaid | 1 | 4 | 3 | nsaid10mg | 1 | 2 | 1 |
| 999 | 200 | 1 | 2 | AXIOM | 1 | 4 | 3 | nsaid | 1 | 4 | 3 | nsaid10mg | 1 | 2 | 1 |
| 175 | 300 | 1 | 1 | AXIOM | 1 | 4 | 3 | nsaid | 1 | 4 | 3 | nsaid25mg | 1 | 1 | 1 |
| 1 | 25 | 1 | 1 | AXIOM | 1 | 4 | 3 | nsaid | 1 | 4 | 3 | nsaid5mg | 1 | 1 | 1 |
| 999 | 200,350 | 2 | 1 | AXIOM,BEXERA | 2 | 3 | 5 | nsaid,potassium | 2 | 3 | 5 | nsaid10mg,potassium12mg | 2 | 1 | 1 |
| 999 | 50,200,350 | 3 | 1 | AXIOM,BEXERA | 2 | 3 | 5 | nsaid,potassium | 2 | 3 | 5 | nsaid10mg,potassium12mg,potassium20mg | 3 | 1 | 1 |
| 999 | 50,200 | 2 | 1 | AXIOM,BEXERA | 2 | 3 | 5 | nsaid,potassium | 2 | 3 | 5 | nsaid10mg,potassium20mg | 2 | 1 | 1 |
| 175 | 50,300 | 2 | 1 | AXIOM,BEXERA | 2 | 3 | 5 | nsaid,potassium | 2 | 3 | 5 | nsaid25mg,potassium20mg | 2 | 1 | 1 |
| 1 | 25,50 | 2 | 1 | AXIOM,BEXERA | 2 | 3 | 5 | nsaid,potassium | 2 | 3 | 5 | nsaid5mg,potassium20mg | 2 | 1 | 1 |
| 999 | 100,200,350 | 3 | 1 | AXIOM,BEXERA,CAZERTA | 3 | 2 | 3 | nsaid,potassium,sodium chloride | 3 | 2 | 3 | nsaid10mg,potassium12mg,sodium chloride10mg | 3 | 1 | 1 |
| 999 | 50,100,200 | 3 | 1 | AXIOM,BEXERA,CAZERTA | 3 | 2 | 3 | nsaid,potassium,sodium chloride | 3 | 2 | 3 | nsaid10mg,potassium20mg,sodium chloride10mg | 3 | 1 | 1 |
| 1 | 25,50,100 | 3 | 1 | AXIOM,BEXERA,CAZERTA | 3 | 2 | 3 | nsaid,potassium,sodium chloride | 3 | 2 | 3 | nsaid5mg,potassium20mg,sodium chloride10mg | 3 | 1 | 1 |
| 999 | 100,200 | 2 | 1 | AXIOM,CAZERTA | 2 | 2 | 2 | nsaid,sodium chloride | 2 | 2 | 2 | nsaid10mg,sodium chloride10mg | 2 | 1 | 1 |
| 1 | 25,100 | 2 | 1 | AXIOM,CAZERTA | 2 | 2 | 2 | nsaid,sodium chloride | 2 | 2 | 2 | nsaid5mg,sodium chloride10mg | 2 | 1 | 1 |
| 201 | 350 | 1 | 2 | BEXERA | 1 | 5 | 4 | potassium | 1 | 5 | 4 | potassium12mg | 1 | 2 | 1 |
| 999 | 350 | 1 | 2 | BEXERA | 1 | 5 | 4 | potassium | 1 | 5 | 4 | potassium12mg | 1 | 2 | 1 |
| 999 | 50,350 | 2 | 1 | BEXERA | 1 | 5 | 4 | potassium | 1 | 5 | 4 | potassium12mg,potassium20mg | 2 | 1 | 1 |
| 378 | 400 | 1 | 1 | BEXERA | 1 | 5 | 4 | potassium | 1 | 5 | 4 | potassium15mg | 1 | 1 | 1 |
| 1 | 50 | 1 | 3 | BEXERA | 1 | 5 | 4 | potassium | 1 | 5 | 4 | potassium20mg | 1 | 3 | 1 |
| 175 | 50 | 1 | 3 | BEXERA | 1 | 5 | 4 | potassium | 1 | 5 | 4 | potassium20mg | 1 | 3 | 1 |
| 999 | 50 | 1 | 3 | BEXERA | 1 | 5 | 4 | potassium | 1 | 5 | 4 | potassium20mg | 1 | 3 | 1 |
| 999 | 50,100,350 | 3 | 1 | BEXERA,CAZERTA | 2 | 4 | 5 | potassium,sodium chloride | 2 | 4 | 5 | potassium12mg,potassium20mg,sodium chloride10mg | 3 | 1 | 1 |
| 999 | 100,350 | 2 | 1 | BEXERA,CAZERTA | 2 | 4 | 5 | potassium,sodium chloride | 2 | 4 | 5 | potassium12mg,sodium chloride10mg | 2 | 1 | 1 |
| 201 | 350,450 | 2 | 1 | BEXERA,CAZERTA | 2 | 4 | 5 | potassium,sodium chloride | 2 | 4 | 5 | potassium12mg,sodium chloride30mg | 2 | 1 | 1 |
| 378 | 100,400 | 2 | 1 | BEXERA,CAZERTA | 2 | 4 | 5 | potassium,sodium chloride | 2 | 4 | 5 | potassium15mg,sodium chloride10mg | 2 | 1 | 1 |
| 1 | 50,100 | 2 | 2 | BEXERA,CAZERTA | 2 | 4 | 5 | potassium,sodium chloride | 2 | 4 | 5 | potassium20mg,sodium chloride10mg | 2 | 2 | 1 |
| 999 | 50,100 | 2 | 2 | BEXERA,CAZERTA | 2 | 4 | 5 | potassium,sodium chloride | 2 | 4 | 5 | potassium20mg,sodium chloride10mg | 2 | 2 | 1 |
| 1 | 100 | 1 | 3 | CAZERTA | 1 | 4 | 2 | sodium chloride | 1 | 4 | 2 | sodium chloride10mg | 1 | 3 | 1 |
| 378 | 100 | 1 | 3 | CAZERTA | 1 | 4 | 2 | sodium chloride | 1 | 4 | 2 | sodium chloride10mg | 1 | 3 | 1 |
| 999 | 100 | 1 | 3 | CAZERTA | 1 | 4 | 2 | sodium chloride | 1 | 4 | 2 | sodium chloride10mg | 1 | 3 | 1 |
| 201 | 450 | 1 | 1 | CAZERTA | 1 | 4 | 2 | sodium chloride | 1 | 4 | 2 | sodium chloride30mg | 1 | 1 | 1 |