Fixing tables due to kable update, adding fix to drug_era script

This commit is contained in:
Clair Blacketer 2022-04-07 11:14:50 -04:00
parent 8451b5027b
commit 800dca15e2
10 changed files with 11322 additions and 11306 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
// Pandoc 2.9 adds attributes on both header and div. We remove the former (to
// be compatible with the behavior of Pandoc < 2.8).
document.addEventListener('DOMContentLoaded', function(e) {
var hs = document.querySelectorAll("div.section[class*='level'] > :first-child");
var i, h, a;
for (i = 0; i < hs.length; i++) {
h = hs[i];
if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6
a = h.attributes;
while (a.length > 0) h.removeAttribute(a[0].name);
}
});

View File

@ -34,7 +34,9 @@ window.initializeCodeFolding = function(show) {
showCodeButton.append(showCodeText); showCodeButton.append(showCodeText);
showCodeButton showCodeButton
.attr('data-toggle', 'collapse') .attr('data-toggle', 'collapse')
.attr('data-bs-toggle', 'collapse') // BS5
.attr('data-target', '#' + id) .attr('data-target', '#' + id)
.attr('data-bs-target', '#' + id) // BS5
.attr('aria-expanded', showThis) .attr('aria-expanded', showThis)
.attr('aria-controls', id); .attr('aria-controls', id);

View File

@ -13,7 +13,7 @@
<title>sqlScripts.knit</title> <title>sqlScripts.knit</title>
<script src="site_libs/header-attrs-2.11/header-attrs.js"></script> <script src="site_libs/header-attrs-2.13/header-attrs.js"></script>
<script src="site_libs/jquery-3.6.0/jquery-3.6.0.min.js"></script> <script src="site_libs/jquery-3.6.0/jquery-3.6.0.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="site_libs/bootstrap-3.3.5/css/cosmo.min.css" rel="stylesheet" /> <link href="site_libs/bootstrap-3.3.5/css/cosmo.min.css" rel="stylesheet" />
@ -63,6 +63,7 @@ if (window.hljs) {
<link rel="stylesheet" href="style.css" type="text/css" /> <link rel="stylesheet" href="style.css" type="text/css" />
@ -88,6 +89,9 @@ button.code-folding-btn:focus {
summary { summary {
display: list-item; display: list-item;
} }
details > summary > p:only-child {
display: inline;
}
pre code { pre code {
padding: 0; padding: 0;
} }
@ -312,7 +316,7 @@ div.tocify {
<div class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-bs-toggle="collapse" data-target="#navbar" data-bs-target="#navbar">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
@ -328,7 +332,7 @@ div.tocify {
</a> </a>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fa fa-landmark"></span> <span class="fa fa-landmark"></span>
Background Background
@ -348,7 +352,7 @@ div.tocify {
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fa fa-list-alt"></span> <span class="fa fa-list-alt"></span>
Conventions Conventions
@ -368,7 +372,7 @@ div.tocify {
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fa fa-history"></span> <span class="fa fa-history"></span>
CDM Versions CDM Versions
@ -386,7 +390,7 @@ div.tocify {
<a href="cdm53.html">CDM v5.3</a> <a href="cdm53.html">CDM v5.3</a>
</li> </li>
<li class="dropdown-submenu"> <li class="dropdown-submenu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">NEW CDM v5.4</a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">NEW CDM v5.4</a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li> <li>
<a href="cdm54.html">CDM v5.4</a> <a href="cdm54.html">CDM v5.4</a>
@ -399,7 +403,7 @@ div.tocify {
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fa fa-plus-square"></span> <span class="fa fa-plus-square"></span>
Proposals Proposals
@ -411,7 +415,7 @@ div.tocify {
<a href="https://github.com/OHDSI/CommonDataModel/issues?q=is%3Aopen+is%3Aissue+label%3AProposal">Under Review</a> <a href="https://github.com/OHDSI/CommonDataModel/issues?q=is%3Aopen+is%3Aissue+label%3AProposal">Under Review</a>
</li> </li>
<li class="dropdown-submenu"> <li class="dropdown-submenu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Accepted</a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">Accepted</a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li> <li>
<a href="https://github.com/OHDSI/CommonDataModel/issues/252">Region_concept_id</a> <a href="https://github.com/OHDSI/CommonDataModel/issues/252">Region_concept_id</a>
@ -421,7 +425,7 @@ div.tocify {
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fa fa-question"></span> <span class="fa fa-question"></span>
How to How to
@ -441,7 +445,7 @@ div.tocify {
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fa fa-life-ring"></span> <span class="fa fa-life-ring"></span>
Support Support
@ -700,174 +704,168 @@ GROUP BY person_id
<h3>Drug Eras</h3> <h3>Drug Eras</h3>
<p>If the DRUG_EXPOSURE table is populated this script will string together periods of time that a person is exposed to an active drug ingredient, allowing for 30 gaps in between. It will then insert the resulting era records into the DRUG_ERA table. <strong>NOTE</strong> This query only works with 5.3 and below.</p> <p>If the DRUG_EXPOSURE table is populated this script will string together periods of time that a person is exposed to an active drug ingredient, allowing for 30 gaps in between. It will then insert the resulting era records into the DRUG_ERA table. <strong>NOTE</strong> This query only works with 5.3 and below.</p>
<pre class="sql"><code> <pre class="sql"><code>
/**************************************************** -- Code taken from:
OHDSI-SQL File Instructions -- https://github.com/OHDSI/ETL-CMS/blob/master/SQL/create_CDMv5_drug_era_non_stockpile.sql
-----------------------------
1. Set parameter name of schema that contains CDMv4 instance
(@SOURCE_CDMV4, @SOURCE_CDMV4_SCHEMA)
2. Set parameter name of schema that contains CDMv5 instance
(@TARGET_CDMV5, @TARGET_CDMV5_SCHEMA)
3. Run this script through SqlRender to produce a script that will work in your
source dialect. SqlRender can be found here: https://github.com/OHDSI/SqlRender
4. Run the script produced by SQL Render on your target RDBDMS.
&lt;RDBMS&gt; File Instructions
-------------------------
1. This script will hold a number of placeholders for your CDM V4 and CDMV5
database/schema. In order to make this file work in your environment, you
should plan to do a global &quot;FIND AND REPLACE&quot; on this file to fill in the
file with values that pertain to your environment. The following are the
tokens you should use when doing your &quot;FIND AND REPLACE&quot; operation:
[CDM]
[CDM].[CDMSCHEMA]
*********************************************************************************/
/* SCRIPT PARAMETERS */
{DEFAULT @TARGET_CDMV5 = &#39;[CDM]&#39; } -- The target CDMv5 database name if object_id(&#39;tempdb..#tmp_de&#39;, &#39;U&#39;) is not null drop table #tmp_de;
{DEFAULT @TARGET_CDMV5_SCHEMA = &#39;[CDM].[CDMSCHEMA]&#39; } -- the target CDMv5 database plus schema
USE @TARGET_CDMV5; WITH
ctePreDrugTarget(drug_exposure_id, person_id, ingredient_concept_id, drug_exposure_start_date, days_supply, drug_exposure_end_date) AS
(-- Normalize DRUG_EXPOSURE_END_DATE to either the existing drug exposure end date, or add days supply, or add 1 day to the start date
SELECT
d.drug_exposure_id
, d.person_id
, c.concept_id AS ingredient_concept_id
, d.drug_exposure_start_date AS drug_exposure_start_date
, d.days_supply AS days_supply
, COALESCE(
---NULLIF returns NULL if both values are the same, otherwise it returns the first parameter
NULLIF(drug_exposure_end_date, NULL),
---If drug_exposure_end_date != NULL, return drug_exposure_end_date, otherwise go to next case
NULLIF(dateadd(day,days_supply,drug_exposure_start_date), drug_exposure_start_date),
---If days_supply != NULL or 0, return drug_exposure_start_date + days_supply, otherwise go to next case
dateadd(day,1,drug_exposure_start_date)
---Add 1 day to the drug_exposure_start_date since there is no end_date or INTERVAL for the days_supply
) AS drug_exposure_end_date
FROM @cdm_schema.drug_exposure d
JOIN @cdm_schema.concept_ancestor ca ON ca.descendant_concept_id = d.drug_concept_id
JOIN @cdm_schema.concept c ON ca.ancestor_concept_id = c.concept_id
WHERE c.vocabulary_id = &#39;RxNorm&#39; ---8 selects RxNorm from the vocabulary_id
AND c.concept_class_id = &#39;Ingredient&#39;
AND d.drug_concept_id != 0 ---Our unmapped drug_concept_id&#39;s are set to 0, so we don&#39;t want different drugs wrapped up in the same era
AND coalesce(d.days_supply,0) &gt;= 0 ---We have cases where days_supply is negative, and this can set the end_date before the start_date, which we don&#39;t want. So we&#39;re just looking over those rows. This is a data-quality issue.
)
, cteSubExposureEndDates (person_id, ingredient_concept_id, end_date) AS --- A preliminary sorting that groups all of the overlapping exposures into one exposure so that we don&#39;t double-count non-gap-days
(
/**** SELECT person_id, ingredient_concept_id, event_date AS end_date
DRUG ERA FROM
Note: Eras derived from DRUG_EXPOSURE table, using 30d gap (
****/ SELECT person_id, ingredient_concept_id, event_date, event_type,
IF OBJECT_ID(&#39;tempdb..#cteDrugTarget&#39;, &#39;U&#39;) IS NOT NULL MAX(start_ordinal) OVER (PARTITION BY person_id, ingredient_concept_id
DROP TABLE #cteDrugTarget; ORDER BY event_date, event_type ROWS unbounded preceding) AS start_ordinal,
-- this pulls the current START down from the prior rows so that the NULLs
/* / */ -- from the END DATES will contain a value we can compare with
ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
-- Normalize DRUG_EXPOSURE_END_DATE to either the existing drug exposure end date, or add days supply, or add 1 day to the start date ORDER BY event_date, event_type) AS overall_ord
SELECT d.DRUG_EXPOSURE_ID -- this re-numbers the inner UNION so all rows are numbered ordered by the event date
,d.PERSON_ID
,c.CONCEPT_ID
,d.DRUG_TYPE_CONCEPT_ID
,DRUG_EXPOSURE_START_DATE
,COALESCE(DRUG_EXPOSURE_END_DATE, DATEADD(day, DAYS_SUPPLY, DRUG_EXPOSURE_START_DATE), DATEADD(day, 1, DRUG_EXPOSURE_START_DATE)) AS DRUG_EXPOSURE_END_DATE
,c.CONCEPT_ID AS INGREDIENT_CONCEPT_ID
INTO #cteDrugTarget
FROM @TARGET_CDMV5_SCHEMA.DRUG_EXPOSURE d
INNER JOIN @TARGET_CDMV5_SCHEMA.CONCEPT_ANCESTOR ca ON ca.DESCENDANT_CONCEPT_ID = d.DRUG_CONCEPT_ID
INNER JOIN @TARGET_CDMV5_SCHEMA.CONCEPT c ON ca.ANCESTOR_CONCEPT_ID = c.CONCEPT_ID
WHERE c.DOMAIN_ID = &#39;Drug&#39;
AND c.CONCEPT_CLASS_ID = &#39;Ingredient&#39;
AND c.STANDARD_CONCEPT = &#39;S&#39;;
/* / */
IF OBJECT_ID(&#39;tempdb..#cteEndDates&#39;, &#39;U&#39;) IS NOT NULL
DROP TABLE #cteEndDates;
/* / */
SELECT PERSON_ID
,INGREDIENT_CONCEPT_ID
,DATEADD(day, - 30, EVENT_DATE) AS END_DATE -- unpad the end date
INTO #cteEndDates
FROM (
SELECT E1.PERSON_ID
,E1.INGREDIENT_CONCEPT_ID
,E1.EVENT_DATE
,COALESCE(E1.START_ORDINAL, MAX(E2.START_ORDINAL)) START_ORDINAL
,E1.OVERALL_ORD
FROM (
SELECT PERSON_ID
,INGREDIENT_CONCEPT_ID
,EVENT_DATE
,EVENT_TYPE
,START_ORDINAL
,ROW_NUMBER() OVER (
PARTITION BY PERSON_ID
,INGREDIENT_CONCEPT_ID ORDER BY EVENT_DATE
,EVENT_TYPE
) AS OVERALL_ORD -- this re-numbers the inner UNION so all rows are numbered ordered by the event date
FROM ( FROM (
-- select the start dates, assigning a row number to each -- select the start dates, assigning a row number to each
SELECT PERSON_ID SELECT person_id, ingredient_concept_id, drug_exposure_start_date AS event_date,
,INGREDIENT_CONCEPT_ID -1 AS event_type,
,DRUG_EXPOSURE_START_DATE AS EVENT_DATE ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
,0 AS EVENT_TYPE ORDER BY drug_exposure_start_date) AS start_ordinal
,ROW_NUMBER() OVER ( FROM ctePreDrugTarget
PARTITION BY PERSON_ID
,INGREDIENT_CONCEPT_ID ORDER BY DRUG_EXPOSURE_START_DATE
) AS START_ORDINAL
FROM #cteDrugTarget
UNION ALL UNION ALL
-- add the end dates with NULL as the row number, padding the end dates by 30 to allow a grace period for overlapping ranges. SELECT person_id, ingredient_concept_id, drug_exposure_end_date, 1 AS event_type, NULL
SELECT PERSON_ID FROM ctePreDrugTarget
,INGREDIENT_CONCEPT_ID ) RAWDATA
,DATEADD(day, 30, DRUG_EXPOSURE_END_DATE) ) e
,1 AS EVENT_TYPE WHERE (2 * e.start_ordinal) - e.overall_ord = 0
,NULL )
FROM #cteDrugTarget
) RAWDATA
) E1
INNER JOIN (
SELECT PERSON_ID
,INGREDIENT_CONCEPT_ID
,DRUG_EXPOSURE_START_DATE AS EVENT_DATE
,ROW_NUMBER() OVER (
PARTITION BY PERSON_ID
,INGREDIENT_CONCEPT_ID ORDER BY DRUG_EXPOSURE_START_DATE
) AS START_ORDINAL
FROM #cteDrugTarget
) E2 ON E1.PERSON_ID = E2.PERSON_ID
AND E1.INGREDIENT_CONCEPT_ID = E2.INGREDIENT_CONCEPT_ID
AND E2.EVENT_DATE &lt;= E1.EVENT_DATE
GROUP BY E1.PERSON_ID
,E1.INGREDIENT_CONCEPT_ID
,E1.EVENT_DATE
,E1.START_ORDINAL
,E1.OVERALL_ORD
) E
WHERE 2 * E.START_ORDINAL - E.OVERALL_ORD = 0;
/* / */ , cteDrugExposureEnds (person_id, drug_concept_id, drug_exposure_start_date, drug_sub_exposure_end_date) AS
(
SELECT
dt.person_id
, dt.ingredient_concept_id
, dt.drug_exposure_start_date
, MIN(e.end_date) AS drug_sub_exposure_end_date
FROM ctePreDrugTarget dt
JOIN cteSubExposureEndDates e ON dt.person_id = e.person_id AND dt.ingredient_concept_id = e.ingredient_concept_id AND e.end_date &gt;= dt.drug_exposure_start_date
GROUP BY
dt.drug_exposure_id
, dt.person_id
, dt.ingredient_concept_id
, dt.drug_exposure_start_date
)
--------------------------------------------------------------------------------------------------------------
, cteSubExposures(row_number, person_id, drug_concept_id, drug_sub_exposure_start_date, drug_sub_exposure_end_date, drug_exposure_count) AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY person_id, drug_concept_id, drug_sub_exposure_end_date ORDER BY person_id)
, person_id, drug_concept_id, MIN(drug_exposure_start_date) AS drug_sub_exposure_start_date, drug_sub_exposure_end_date, COUNT(*) AS drug_exposure_count
FROM cteDrugExposureEnds
GROUP BY person_id, drug_concept_id, drug_sub_exposure_end_date
--ORDER BY person_id, drug_concept_id
)
--------------------------------------------------------------------------------------------------------------
/*Everything above grouped exposures into sub_exposures if there was overlap between exposures.
*So there was no persistence window. Now we can add the persistence window to calculate eras.
*/
--------------------------------------------------------------------------------------------------------------
, cteFinalTarget(row_number, person_id, ingredient_concept_id, drug_sub_exposure_start_date, drug_sub_exposure_end_date, drug_exposure_count, days_exposed) AS
(
SELECT row_number, person_id, drug_concept_id, drug_sub_exposure_start_date, drug_sub_exposure_end_date, drug_exposure_count
, datediff(day,drug_sub_exposure_start_date,drug_sub_exposure_end_date) AS days_exposed
FROM cteSubExposures
)
--------------------------------------------------------------------------------------------------------------
, cteEndDates (person_id, ingredient_concept_id, end_date) AS -- the magic
(
SELECT person_id, ingredient_concept_id, dateadd(day,-30,event_date) AS end_date -- unpad the end date
FROM
(
SELECT person_id, ingredient_concept_id, event_date, event_type,
MAX(start_ordinal) OVER (PARTITION BY person_id, ingredient_concept_id
ORDER BY event_date, event_type ROWS UNBOUNDED PRECEDING) AS start_ordinal,
-- this pulls the current START down from the prior rows so that the NULLs
-- from the END DATES will contain a value we can compare with
ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
ORDER BY event_date, event_type) AS overall_ord
-- this re-numbers the inner UNION so all rows are numbered ordered by the event date
FROM (
-- select the start dates, assigning a row number to each
SELECT person_id, ingredient_concept_id, drug_sub_exposure_start_date AS event_date,
-1 AS event_type,
ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
ORDER BY drug_sub_exposure_start_date) AS start_ordinal
FROM cteFinalTarget
IF OBJECT_ID(&#39;tempdb..#cteDrugExpEnds&#39;, &#39;U&#39;) IS NOT NULL UNION ALL
DROP TABLE #cteDrugExpEnds;
/* / */ -- pad the end dates by 30 to allow a grace period for overlapping ranges.
SELECT person_id, ingredient_concept_id, dateadd(day,30,drug_sub_exposure_end_date), 1 AS event_type, NULL
FROM cteFinalTarget
) RAWDATA
) e
WHERE (2 * e.start_ordinal) - e.overall_ord = 0
SELECT d.PERSON_ID )
,d.INGREDIENT_CONCEPT_ID , cteDrugEraEnds (person_id, drug_concept_id, drug_sub_exposure_start_date, drug_era_end_date, drug_exposure_count, days_exposed) AS
,d.DRUG_TYPE_CONCEPT_ID (
,d.DRUG_EXPOSURE_START_DATE SELECT
,MIN(e.END_DATE) AS ERA_END_DATE ft.person_id
INTO #cteDrugExpEnds , ft.ingredient_concept_id
FROM #cteDrugTarget d , ft.drug_sub_exposure_start_date
INNER JOIN #cteEndDates e ON d.PERSON_ID = e.PERSON_ID , MIN(e.end_date) AS era_end_date
AND d.INGREDIENT_CONCEPT_ID = e.INGREDIENT_CONCEPT_ID , drug_exposure_count
AND e.END_DATE &gt;= d.DRUG_EXPOSURE_START_DATE , days_exposed
GROUP BY d.PERSON_ID FROM cteFinalTarget ft
,d.INGREDIENT_CONCEPT_ID JOIN cteEndDates e ON ft.person_id = e.person_id AND ft.ingredient_concept_id = e.ingredient_concept_id AND e.end_date &gt;= ft.drug_sub_exposure_start_date
,d.DRUG_TYPE_CONCEPT_ID GROUP BY
,d.DRUG_EXPOSURE_START_DATE; ft.person_id
, ft.ingredient_concept_id
, ft.drug_sub_exposure_start_date
, drug_exposure_count
, days_exposed
)
SELECT
row_number()over(order by person_id) drug_era_id
, person_id
, drug_concept_id
, MIN(drug_sub_exposure_start_date) AS drug_era_start_date
, drug_era_end_date
, SUM(drug_exposure_count) AS drug_exposure_count
, datediff(day,MIN(drug_sub_exposure_start_date),drug_era_end_date)-SUM(days_exposed) as gap_days
INTO #tmp_de
FROM cteDrugEraEnds dee
GROUP BY person_id, drug_concept_id, drug_era_end_date;
/* / */ INSERT INTO @cdm_schema.drug_era(drug_era_id,person_id, drug_concept_id, drug_era_start_date, drug_era_end_date, drug_exposure_count, gap_days)
SELECT * FROM #tmp_de;</code></pre>
INSERT INTO @TARGET_CDMV5_SCHEMA.drug_era
SELECT row_number() OVER (
ORDER BY person_id
) AS drug_era_id
,person_id
,INGREDIENT_CONCEPT_ID
,min(DRUG_EXPOSURE_START_DATE) AS drug_era_start_date
,ERA_END_DATE
,COUNT(*) AS DRUG_EXPOSURE_COUNT
,30 AS gap_days
FROM #cteDrugExpEnds
GROUP BY person_id
,INGREDIENT_CONCEPT_ID
,drug_type_concept_id
,ERA_END_DATE;
</code></pre>
</div> </div>
</div> </div>
<div id="example-etl-script" class="section level2"> <div id="example-etl-script" class="section level2">

View File

@ -92,9 +92,9 @@ for(tb in tables) {
print(kable(x = loopTable, align = "l", row.names = FALSE, format = "html", escape = FALSE) %>% print(kable(x = loopTable, align = "l", row.names = FALSE, format = "html", escape = FALSE) %>%
column_spec(1, bold = T) %>% column_spec(1, bold = T) %>%
column_spec(2, width = "3in", include_thead = T) %>% # column_spec(2, width = "3in", include_thead = T) %>%
column_spec(3, width = "4in", include_thead = T) %>% # column_spec(3, width = "4in", include_thead = T) %>%
column_spec(4:9, width = "1in", include_thead = T) %>% # column_spec(4:9, width = "1in", include_thead = T) %>%
kable_styling(c("condensed","hover"), position = "center", full_width = T, font_size = 13)) kable_styling(c("condensed","hover"), position = "center", full_width = T, font_size = 13))

View File

@ -108,9 +108,9 @@ for(tb in tables) {
print(kable(x = loopTable, align = "l", row.names = FALSE, format = "html", escape = FALSE) %>% print(kable(x = loopTable, align = "l", row.names = FALSE, format = "html", escape = FALSE) %>%
column_spec(1, bold = T) %>% column_spec(1, bold = T) %>%
column_spec(2, width = "3in", include_thead = T) %>% # column_spec(2, width = "3in", include_thead = T) %>%
column_spec(3, width = "4in", include_thead = T) %>% # column_spec(3, width = "4in", include_thead = T) %>%
column_spec(4:9, width = "1in", include_thead = T) %>% # column_spec(4:9, width = "1in", include_thead = T) %>%
kable_styling(c("condensed","hover"), position = "center", full_width = T, font_size = 13)) kable_styling(c("condensed","hover"), position = "center", full_width = T, font_size = 13))

View File

@ -104,9 +104,9 @@ for(tb in tables) {
print(kable(x = loopTable, align = "l", row.names = FALSE, format = "html") %>% print(kable(x = loopTable, align = "l", row.names = FALSE, format = "html") %>%
column_spec(1, bold = T) %>% column_spec(1, bold = T) %>%
column_spec(2, width = "3in", include_thead = T) %>% # column_spec(2, width = "3in", include_thead = T) %>%
column_spec(3, width = "4in", include_thead = T) %>% # column_spec(3, width = "4in", include_thead = T) %>%
column_spec(4:9, width = "1in", include_thead = T) %>% # column_spec(4:9, width = "1in", include_thead = T) %>%
kable_styling(c("condensed","hover"), position = "center", full_width = T, font_size = 13)) kable_styling(c("condensed","hover"), position = "center", full_width = T, font_size = 13))

View File

@ -232,178 +232,172 @@ GROUP BY person_id
### Drug Eras ### Drug Eras
If the DRUG_EXPOSURE table is populated this script will string together periods of time that a person is exposed to an active drug ingredient, allowing for 30 gaps in between. It will then insert the resulting era records into the DRUG_ERA table. **NOTE** This query only works with 5.3 and below. If the DRUG_EXPOSURE table is populated this script will string together periods of time that a person is exposed to an active drug ingredient, allowing for 30 gaps in between. It will then insert the resulting era records into the DRUG_ERA table. **NOTE** This query only works with the **version 5** series and below.
```{sql eval = FALSE, echo = TRUE} ```{sql eval = FALSE, echo = TRUE}
/**************************************************** -- Code taken from:
OHDSI-SQL File Instructions -- https://github.com/OHDSI/ETL-CMS/blob/master/SQL/create_CDMv5_drug_era_non_stockpile.sql
-----------------------------
1. Set parameter name of schema that contains CDMv4 instance
(@SOURCE_CDMV4, @SOURCE_CDMV4_SCHEMA)
2. Set parameter name of schema that contains CDMv5 instance
(@TARGET_CDMV5, @TARGET_CDMV5_SCHEMA)
3. Run this script through SqlRender to produce a script that will work in your
source dialect. SqlRender can be found here: https://github.com/OHDSI/SqlRender
4. Run the script produced by SQL Render on your target RDBDMS.
<RDBMS> File Instructions
-------------------------
1. This script will hold a number of placeholders for your CDM V4 and CDMV5
database/schema. In order to make this file work in your environment, you
should plan to do a global "FIND AND REPLACE" on this file to fill in the
file with values that pertain to your environment. The following are the
tokens you should use when doing your "FIND AND REPLACE" operation:
[CDM]
[CDM].[CDMSCHEMA]
*********************************************************************************/
/* SCRIPT PARAMETERS */
{DEFAULT @TARGET_CDMV5 = '[CDM]' } -- The target CDMv5 database name if object_id('tempdb..#tmp_de', 'U') is not null drop table #tmp_de;
{DEFAULT @TARGET_CDMV5_SCHEMA = '[CDM].[CDMSCHEMA]' } -- the target CDMv5 database plus schema
USE @TARGET_CDMV5; WITH
ctePreDrugTarget(drug_exposure_id, person_id, ingredient_concept_id, drug_exposure_start_date, days_supply, drug_exposure_end_date) AS
(-- Normalize DRUG_EXPOSURE_END_DATE to either the existing drug exposure end date, or add days supply, or add 1 day to the start date
SELECT
d.drug_exposure_id
, d.person_id
, c.concept_id AS ingredient_concept_id
, d.drug_exposure_start_date AS drug_exposure_start_date
, d.days_supply AS days_supply
, COALESCE(
---NULLIF returns NULL if both values are the same, otherwise it returns the first parameter
NULLIF(drug_exposure_end_date, NULL),
---If drug_exposure_end_date != NULL, return drug_exposure_end_date, otherwise go to next case
NULLIF(dateadd(day,days_supply,drug_exposure_start_date), drug_exposure_start_date),
---If days_supply != NULL or 0, return drug_exposure_start_date + days_supply, otherwise go to next case
dateadd(day,1,drug_exposure_start_date)
---Add 1 day to the drug_exposure_start_date since there is no end_date or INTERVAL for the days_supply
) AS drug_exposure_end_date
FROM @cdm_schema.drug_exposure d
JOIN @cdm_schema.concept_ancestor ca ON ca.descendant_concept_id = d.drug_concept_id
JOIN @cdm_schema.concept c ON ca.ancestor_concept_id = c.concept_id
WHERE c.vocabulary_id = 'RxNorm' ---8 selects RxNorm from the vocabulary_id
AND c.concept_class_id = 'Ingredient'
AND d.drug_concept_id != 0 ---Our unmapped drug_concept_id's are set to 0, so we don't want different drugs wrapped up in the same era
AND coalesce(d.days_supply,0) >= 0 ---We have cases where days_supply is negative, and this can set the end_date before the start_date, which we don't want. So we're just looking over those rows. This is a data-quality issue.
)
, cteSubExposureEndDates (person_id, ingredient_concept_id, end_date) AS --- A preliminary sorting that groups all of the overlapping exposures into one exposure so that we don't double-count non-gap-days
(
/**** SELECT person_id, ingredient_concept_id, event_date AS end_date
DRUG ERA FROM
Note: Eras derived from DRUG_EXPOSURE table, using 30d gap (
****/ SELECT person_id, ingredient_concept_id, event_date, event_type,
IF OBJECT_ID('tempdb..#cteDrugTarget', 'U') IS NOT NULL MAX(start_ordinal) OVER (PARTITION BY person_id, ingredient_concept_id
DROP TABLE #cteDrugTarget; ORDER BY event_date, event_type ROWS unbounded preceding) AS start_ordinal,
-- this pulls the current START down from the prior rows so that the NULLs
/* / */ -- from the END DATES will contain a value we can compare with
ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
-- Normalize DRUG_EXPOSURE_END_DATE to either the existing drug exposure end date, or add days supply, or add 1 day to the start date ORDER BY event_date, event_type) AS overall_ord
SELECT d.DRUG_EXPOSURE_ID -- this re-numbers the inner UNION so all rows are numbered ordered by the event date
,d.PERSON_ID
,c.CONCEPT_ID
,d.DRUG_TYPE_CONCEPT_ID
,DRUG_EXPOSURE_START_DATE
,COALESCE(DRUG_EXPOSURE_END_DATE, DATEADD(day, DAYS_SUPPLY, DRUG_EXPOSURE_START_DATE), DATEADD(day, 1, DRUG_EXPOSURE_START_DATE)) AS DRUG_EXPOSURE_END_DATE
,c.CONCEPT_ID AS INGREDIENT_CONCEPT_ID
INTO #cteDrugTarget
FROM @TARGET_CDMV5_SCHEMA.DRUG_EXPOSURE d
INNER JOIN @TARGET_CDMV5_SCHEMA.CONCEPT_ANCESTOR ca ON ca.DESCENDANT_CONCEPT_ID = d.DRUG_CONCEPT_ID
INNER JOIN @TARGET_CDMV5_SCHEMA.CONCEPT c ON ca.ANCESTOR_CONCEPT_ID = c.CONCEPT_ID
WHERE c.DOMAIN_ID = 'Drug'
AND c.CONCEPT_CLASS_ID = 'Ingredient'
AND c.STANDARD_CONCEPT = 'S';
/* / */
IF OBJECT_ID('tempdb..#cteEndDates', 'U') IS NOT NULL
DROP TABLE #cteEndDates;
/* / */
SELECT PERSON_ID
,INGREDIENT_CONCEPT_ID
,DATEADD(day, - 30, EVENT_DATE) AS END_DATE -- unpad the end date
INTO #cteEndDates
FROM (
SELECT E1.PERSON_ID
,E1.INGREDIENT_CONCEPT_ID
,E1.EVENT_DATE
,COALESCE(E1.START_ORDINAL, MAX(E2.START_ORDINAL)) START_ORDINAL
,E1.OVERALL_ORD
FROM (
SELECT PERSON_ID
,INGREDIENT_CONCEPT_ID
,EVENT_DATE
,EVENT_TYPE
,START_ORDINAL
,ROW_NUMBER() OVER (
PARTITION BY PERSON_ID
,INGREDIENT_CONCEPT_ID ORDER BY EVENT_DATE
,EVENT_TYPE
) AS OVERALL_ORD -- this re-numbers the inner UNION so all rows are numbered ordered by the event date
FROM ( FROM (
-- select the start dates, assigning a row number to each -- select the start dates, assigning a row number to each
SELECT PERSON_ID SELECT person_id, ingredient_concept_id, drug_exposure_start_date AS event_date,
,INGREDIENT_CONCEPT_ID -1 AS event_type,
,DRUG_EXPOSURE_START_DATE AS EVENT_DATE ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
,0 AS EVENT_TYPE ORDER BY drug_exposure_start_date) AS start_ordinal
,ROW_NUMBER() OVER ( FROM ctePreDrugTarget
PARTITION BY PERSON_ID
,INGREDIENT_CONCEPT_ID ORDER BY DRUG_EXPOSURE_START_DATE
) AS START_ORDINAL
FROM #cteDrugTarget
UNION ALL UNION ALL
-- add the end dates with NULL as the row number, padding the end dates by 30 to allow a grace period for overlapping ranges. SELECT person_id, ingredient_concept_id, drug_exposure_end_date, 1 AS event_type, NULL
SELECT PERSON_ID FROM ctePreDrugTarget
,INGREDIENT_CONCEPT_ID ) RAWDATA
,DATEADD(day, 30, DRUG_EXPOSURE_END_DATE) ) e
,1 AS EVENT_TYPE WHERE (2 * e.start_ordinal) - e.overall_ord = 0
,NULL )
FROM #cteDrugTarget
) RAWDATA
) E1
INNER JOIN (
SELECT PERSON_ID
,INGREDIENT_CONCEPT_ID
,DRUG_EXPOSURE_START_DATE AS EVENT_DATE
,ROW_NUMBER() OVER (
PARTITION BY PERSON_ID
,INGREDIENT_CONCEPT_ID ORDER BY DRUG_EXPOSURE_START_DATE
) AS START_ORDINAL
FROM #cteDrugTarget
) E2 ON E1.PERSON_ID = E2.PERSON_ID
AND E1.INGREDIENT_CONCEPT_ID = E2.INGREDIENT_CONCEPT_ID
AND E2.EVENT_DATE <= E1.EVENT_DATE
GROUP BY E1.PERSON_ID
,E1.INGREDIENT_CONCEPT_ID
,E1.EVENT_DATE
,E1.START_ORDINAL
,E1.OVERALL_ORD
) E
WHERE 2 * E.START_ORDINAL - E.OVERALL_ORD = 0;
/* / */ , cteDrugExposureEnds (person_id, drug_concept_id, drug_exposure_start_date, drug_sub_exposure_end_date) AS
(
SELECT
dt.person_id
, dt.ingredient_concept_id
, dt.drug_exposure_start_date
, MIN(e.end_date) AS drug_sub_exposure_end_date
FROM ctePreDrugTarget dt
JOIN cteSubExposureEndDates e ON dt.person_id = e.person_id AND dt.ingredient_concept_id = e.ingredient_concept_id AND e.end_date >= dt.drug_exposure_start_date
GROUP BY
dt.drug_exposure_id
, dt.person_id
, dt.ingredient_concept_id
, dt.drug_exposure_start_date
)
--------------------------------------------------------------------------------------------------------------
, cteSubExposures(row_number, person_id, drug_concept_id, drug_sub_exposure_start_date, drug_sub_exposure_end_date, drug_exposure_count) AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY person_id, drug_concept_id, drug_sub_exposure_end_date ORDER BY person_id)
, person_id, drug_concept_id, MIN(drug_exposure_start_date) AS drug_sub_exposure_start_date, drug_sub_exposure_end_date, COUNT(*) AS drug_exposure_count
FROM cteDrugExposureEnds
GROUP BY person_id, drug_concept_id, drug_sub_exposure_end_date
--ORDER BY person_id, drug_concept_id
)
--------------------------------------------------------------------------------------------------------------
/*Everything above grouped exposures into sub_exposures if there was overlap between exposures.
*So there was no persistence window. Now we can add the persistence window to calculate eras.
*/
--------------------------------------------------------------------------------------------------------------
, cteFinalTarget(row_number, person_id, ingredient_concept_id, drug_sub_exposure_start_date, drug_sub_exposure_end_date, drug_exposure_count, days_exposed) AS
(
SELECT row_number, person_id, drug_concept_id, drug_sub_exposure_start_date, drug_sub_exposure_end_date, drug_exposure_count
, datediff(day,drug_sub_exposure_start_date,drug_sub_exposure_end_date) AS days_exposed
FROM cteSubExposures
)
--------------------------------------------------------------------------------------------------------------
, cteEndDates (person_id, ingredient_concept_id, end_date) AS -- the magic
(
SELECT person_id, ingredient_concept_id, dateadd(day,-30,event_date) AS end_date -- unpad the end date
FROM
(
SELECT person_id, ingredient_concept_id, event_date, event_type,
MAX(start_ordinal) OVER (PARTITION BY person_id, ingredient_concept_id
ORDER BY event_date, event_type ROWS UNBOUNDED PRECEDING) AS start_ordinal,
-- this pulls the current START down from the prior rows so that the NULLs
-- from the END DATES will contain a value we can compare with
ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
ORDER BY event_date, event_type) AS overall_ord
-- this re-numbers the inner UNION so all rows are numbered ordered by the event date
FROM (
-- select the start dates, assigning a row number to each
SELECT person_id, ingredient_concept_id, drug_sub_exposure_start_date AS event_date,
-1 AS event_type,
ROW_NUMBER() OVER (PARTITION BY person_id, ingredient_concept_id
ORDER BY drug_sub_exposure_start_date) AS start_ordinal
FROM cteFinalTarget
IF OBJECT_ID('tempdb..#cteDrugExpEnds', 'U') IS NOT NULL UNION ALL
DROP TABLE #cteDrugExpEnds;
/* / */ -- pad the end dates by 30 to allow a grace period for overlapping ranges.
SELECT person_id, ingredient_concept_id, dateadd(day,30,drug_sub_exposure_end_date), 1 AS event_type, NULL
FROM cteFinalTarget
) RAWDATA
) e
WHERE (2 * e.start_ordinal) - e.overall_ord = 0
SELECT d.PERSON_ID )
,d.INGREDIENT_CONCEPT_ID , cteDrugEraEnds (person_id, drug_concept_id, drug_sub_exposure_start_date, drug_era_end_date, drug_exposure_count, days_exposed) AS
,d.DRUG_TYPE_CONCEPT_ID (
,d.DRUG_EXPOSURE_START_DATE SELECT
,MIN(e.END_DATE) AS ERA_END_DATE ft.person_id
INTO #cteDrugExpEnds , ft.ingredient_concept_id
FROM #cteDrugTarget d , ft.drug_sub_exposure_start_date
INNER JOIN #cteEndDates e ON d.PERSON_ID = e.PERSON_ID , MIN(e.end_date) AS era_end_date
AND d.INGREDIENT_CONCEPT_ID = e.INGREDIENT_CONCEPT_ID , drug_exposure_count
AND e.END_DATE >= d.DRUG_EXPOSURE_START_DATE , days_exposed
GROUP BY d.PERSON_ID FROM cteFinalTarget ft
,d.INGREDIENT_CONCEPT_ID JOIN cteEndDates e ON ft.person_id = e.person_id AND ft.ingredient_concept_id = e.ingredient_concept_id AND e.end_date >= ft.drug_sub_exposure_start_date
,d.DRUG_TYPE_CONCEPT_ID GROUP BY
,d.DRUG_EXPOSURE_START_DATE; ft.person_id
, ft.ingredient_concept_id
/* / */ , ft.drug_sub_exposure_start_date
, drug_exposure_count
INSERT INTO @TARGET_CDMV5_SCHEMA.drug_era , days_exposed
SELECT row_number() OVER ( )
ORDER BY person_id SELECT
) AS drug_era_id row_number()over(order by person_id) drug_era_id
,person_id , person_id
,INGREDIENT_CONCEPT_ID , drug_concept_id
,min(DRUG_EXPOSURE_START_DATE) AS drug_era_start_date , MIN(drug_sub_exposure_start_date) AS drug_era_start_date
,ERA_END_DATE , drug_era_end_date
,COUNT(*) AS DRUG_EXPOSURE_COUNT , SUM(drug_exposure_count) AS drug_exposure_count
,30 AS gap_days , datediff(day,MIN(drug_sub_exposure_start_date),drug_era_end_date)-SUM(days_exposed) as gap_days
FROM #cteDrugExpEnds INTO #tmp_de
GROUP BY person_id FROM cteDrugEraEnds dee
,INGREDIENT_CONCEPT_ID GROUP BY person_id, drug_concept_id, drug_era_end_date;
,drug_type_concept_id
,ERA_END_DATE;
INSERT INTO @cdm_schema.drug_era(drug_era_id,person_id, drug_concept_id, drug_era_start_date, drug_era_end_date, drug_exposure_count, gap_days)
SELECT * FROM #tmp_de;
``` ```
## **Example ETL Script** ## **Example ETL Script**