<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://wiki.voror.co.uk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=AndreHui</id>
	<title>Voror_Wiki - User contributions [en-gb]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.voror.co.uk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=AndreHui"/>
	<link rel="alternate" type="text/html" href="https://wiki.voror.co.uk/index.php?title=Special:Contributions/AndreHui"/>
	<updated>2026-10-23T16:57:37Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.46.2</generator>
	<entry>
		<id>https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_2)&amp;diff=1577</id>
		<title>Remote Subscriber Database (RSD) SQL guide (Compass 2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_2)&amp;diff=1577"/>
		<updated>2020-06-18T09:38:53Z</updated>

		<summary type="html">&lt;p&gt;AndreHui: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This article describes the steps needed to create, and analyse, patient data selections from the Discovery database.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This outlines the steps we take to create the patient selection for analysis from the Discovery database.&lt;br /&gt;
&lt;br /&gt;
To add this data to a report, we create datasets. Each dataset represents the result set from running a combination of SQL commands and stored procedures on a data source.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 1.1 shows the algorithm we follow to generate these datasets&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD SQL Guide C2 Fig 1.1.jpg|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%201.1.jpg|frameless|633x633px]]&lt;br /&gt;
&lt;br /&gt;
= Creating the Patient Cohort =&lt;br /&gt;
This is the first step when we receive a list of patients marked for observational study.&lt;br /&gt;
&lt;br /&gt;
===== General Patient Cohort =====&lt;br /&gt;
&#039;&#039;&#039;Fig 2.1 Entity diagram showing the relationships between patient and organization&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD SQL Guide C2 Fig 2.1.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%202.1.png|frameless|494x494px]]&lt;br /&gt;
&lt;br /&gt;
We filter patients by their organization ODS codes to ensure only patients of the required GP practices are selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example 1.1 Building patient cohort SQL with NHS numbers&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS cohort_patient;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_patient AS&lt;br /&gt;
SELECT&lt;br /&gt;
       p.id AS patient_id,&lt;br /&gt;
       p.nhs_number,&lt;br /&gt;
       p.person_id AS group_by,&lt;br /&gt;
       p.organization_id,&lt;br /&gt;
       p.date_of_birth -- add additional columns if required   &lt;br /&gt;
FROM patient p JOIN organization org ON p.organization_id = org.id&lt;br /&gt;
WHERE org.ods_code IN (&#039;EMIS99&#039;,&#039;5PC64&#039;);  &lt;br /&gt;
&lt;br /&gt;
-- add ods codes inside the brackets e.g. EMIS99, 5PC64&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Cohort with Filtered Observations =====&lt;br /&gt;
Sometimes, the cohort is created with filtered observations.&lt;br /&gt;
&lt;br /&gt;
In the example below, we create a cohort where the patients are registered, alive and have diabetes. They belong to age 12 and over.&lt;br /&gt;
&lt;br /&gt;
Here we have introduced additional tables for the query: observation, concept and the episode of care.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 2.2 Entity diagram showing the relationships between patient, observation and episode of care&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD SQL Guide C2 Fig 2.2.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%202.2.png|frameless|691x691px]]&lt;br /&gt;
&lt;br /&gt;
By joining the observation table we can obtain the observational information about the patient. If specific information is required, like in this case we are looking for patients with diabetes, we would need to filter the observation based on that specific clinical term. That could be in the form of a stand alone code or a list of codes: SNOMED, READ, CTV3, etc.&lt;br /&gt;
&lt;br /&gt;
The observation table does not store these codes directly but store them as concept ids. On querying the data then, the concept table will be queried first to get the list of concept ids that you are interested in for this observation.&lt;br /&gt;
&lt;br /&gt;
If you intend to filter observations by SNOMEDS then add in the concept map table which will map any legacy concept ids, that is, READ, CTV3 etc, to the SNOMED concept ids, then you can filter by SNOMED codes as shown: &amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
FROM patient p&lt;br /&gt;
JOIN observation o on o.patient_id = p.id&lt;br /&gt;
JOIN concept_map cm on cm.legacy = o.non_core_concept_id&lt;br /&gt;
-- use the non core concept id for link to the concept table&lt;br /&gt;
JOIN concept c on c.dbid = cm.core&lt;br /&gt;
WHERE c.code IN (46635009, 44054006) -- e.g. diabetes snomed codes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From the episode of care, we can obtain whether the patient is currently registered and has the appropriate registration type for this study:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
FROM patient p&lt;br /&gt;
JOIN episode_of_care e ON e.patient_id = p.id&lt;br /&gt;
JOIN concept c ON c.dbid = e.registration_type_concept_id&lt;br /&gt;
WHERE c.code = &#039;R&#039;  -- registered&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Some patients prefer to withhold consent and therefore are excluded from the cohort. We can check this in the observation table. Again we need to get a list of concept ids that you are interested in for this observation.&lt;br /&gt;
&lt;br /&gt;
For example, you could approach it in this way:&lt;br /&gt;
&lt;br /&gt;
1.        Create a lookup table to hold the consent codes:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Consent_Codes.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Consent%20Codes.png|alt=|frameless|448x448px]]&lt;br /&gt;
&lt;br /&gt;
2.        Create a view to filter out the concept ids on the observation table with these codes:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
-- filter out the observation table with the relevant consent codes&lt;br /&gt;
&lt;br /&gt;
DROP VIEW IF EXISTS obs_optout_view;&lt;br /&gt;
&lt;br /&gt;
CREATE VIEW obs_optout_view AS&lt;br /&gt;
SELECT&lt;br /&gt;
       ob.id,&lt;br /&gt;
       ob.patient_id,&lt;br /&gt;
       ob.organization_id,&lt;br /&gt;
       ob.non_core_concept_id,&lt;br /&gt;
       ob.clinical_effective_date,&lt;br /&gt;
       cpt.dbid,&lt;br /&gt;
       cpt.code,&lt;br /&gt;
       cpt.name,&lt;br /&gt;
       csc.status&lt;br /&gt;
FROM observation ob&lt;br /&gt;
JOIN concept cpt ON cpt.dbid = ob.non_core_concept_id&lt;br /&gt;
JOIN consent_code csc ON cpt.id = csc.code&lt;br /&gt;
AND cpt.id = BINARY csc.code;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3.        Using the view to create a helper function to get the latest opt-out status of the patient:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
-- function to retrieve the patient&#039;s latest opt out status&lt;br /&gt;
&lt;br /&gt;
DROP FUNCTION IF EXISTS get_optout_status;&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE FUNCTION get_optout_status(p_patient_id BIGINT,&lt;br /&gt;
                                  p_organization_id BIGINT)&lt;br /&gt;
RETURNS VARCHAR(1)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_status          VARCHAR(1);&lt;br /&gt;
DECLARE l_patient_id      BIGINT;&lt;br /&gt;
DECLARE l_organization_id BIGINT;&lt;br /&gt;
&lt;br /&gt;
  SELECT&lt;br /&gt;
         ob.patient_id,&lt;br /&gt;
         ob.organization_id,&lt;br /&gt;
         IF (ob.status = &#039;Opt-Out&#039;, 1, IF(ob.status = &#039;Opt-In&#039;,null,null))&lt;br /&gt;
         INTO l_patient_id, l_organization_id, l_status&lt;br /&gt;
  FROM obs_optout_view ob&lt;br /&gt;
  LEFT JOIN obs_optout_view ob2 ON ob2.patient_id = ob.patient_id&lt;br /&gt;
  AND (ob.clinical_effective_date &amp;lt; ob2.clinical_effective_date&lt;br /&gt;
  OR (ob.clinical_effective_date = ob2.clinical_effective_date&lt;br /&gt;
       AND ob.id &amp;lt; ob2.id))&lt;br /&gt;
  WHERE ob2.patient_id IS NULL&lt;br /&gt;
  AND ob.patient_id = p_patient_id&lt;br /&gt;
  AND ob.organization_id = p_organization_id LIMIT 1;&lt;br /&gt;
&lt;br /&gt;
RETURN l_status;&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Where 1 = opt out, null = opt in or not recorded derived from the latest observation from the consent codes.&lt;br /&gt;
&lt;br /&gt;
Putting all of the above together, patient cohort query becomes this:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_patient AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
         p.id         AS patient_id,&lt;br /&gt;
         p.person_id  AS group_by,&lt;br /&gt;
         get_optout_status (p.id, p.registered_practice_organization_id) AS optout_status,&lt;br /&gt;
         IF (p.date_of_death IS NULL,&lt;br /&gt;
              TIMESTAMPDIFF(YEAR, p.date_of_birth, CURDATE()),&lt;br /&gt;
              TIMESTAMPDIFF(YEAR, p.date_of_birth, p.date_of_death)) age_years&lt;br /&gt;
  FROM patient p&lt;br /&gt;
  JOIN observation o ON p.id = o.patient_id&lt;br /&gt;
  JOIN organization org ON org.id = o.organization_id&lt;br /&gt;
  JOIN episode_of_care e ON e.patient_id = o.patient_id&lt;br /&gt;
  JOIN concept c ON c.dbid = e.registration_type_concept_id&lt;br /&gt;
  JOIN (SELECT o.patient_id&lt;br /&gt;
        FROM observation o&lt;br /&gt;
        JOIN concept_map cm ON cm.legacy = o.non_core_concept_id&lt;br /&gt;
        JOIN concept c ON c.dbid = cm.core&lt;br /&gt;
        WHERE c.code IN (46635009, 44054006) -- e.g. snomed codes&lt;br /&gt;
        ) ob ON ob.patient_id = p.id&lt;br /&gt;
  WHERE org.ods_code IN (&#039;EMIS99&#039;,&#039;5PC64&#039;) -- e.g. ods codes&lt;br /&gt;
  AND p.date_of_death IS NULL&lt;br /&gt;
  AND c.code = &#039;R&#039;  -- registered patient&lt;br /&gt;
  AND e.date_registered &amp;lt;= now()&lt;br /&gt;
  AND (e.date_registered_end &amp;gt; now() OR&lt;br /&gt;
       e.date_registered_end IS NULL) -- active&lt;br /&gt;
  AND IF (p.date_of_death IS NULL,&lt;br /&gt;
          TIMESTAMPDIFF(YEAR, p.date_of_birth, CURDATE()),&lt;br /&gt;
          TIMESTAMPDIFF(YEAR, p.date_of_birth, p.date_of_death)) &amp;gt;= 12 -- age 12 and over&lt;br /&gt;
  AND get_optout_status(p.id, p.registered_practice_organization_id) IS NULL; -- opt in status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Creating the Observations Cohort =&lt;br /&gt;
We create a separate observations cohort for query performance. Instead of scanning through the entire observation table, we create a sub-set of the observation table based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example 2.1 Building the Observations Cohort SQL&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_observations AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
         o.id,&lt;br /&gt;
         o.person_id AS group_by,&lt;br /&gt;
         o.patient_id,&lt;br /&gt;
         o.person_id,&lt;br /&gt;
         o.clinical_effective_date,&lt;br /&gt;
         o.non_core_concept_id,&lt;br /&gt;
         o.result_value,&lt;br /&gt;
         o.result_value_units,&lt;br /&gt;
         o.result_date,&lt;br /&gt;
         o.result_text,&lt;br /&gt;
         cr.age_year&lt;br /&gt;
  FROM observation o JOIN cohort_patient cr&lt;br /&gt;
  ON o.person_id = cr.person_id&lt;br /&gt;
  AND o.organization_id = cr.organization_id&lt;br /&gt;
  AND o.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX obs_cpt_ix ON cohort_observation(non_core_concept_id);&lt;br /&gt;
CREATE INDEX obs_pat_ix ON cohort_observation(patient_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Creating the Medications Cohort =&lt;br /&gt;
Again, we create the medications cohort for query performance. We create a sub-set of the medication statement/order table based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 4.1 Entity diagram showing the relationships between patient, medication statement and organization&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Fig_4.1.png|link=https://wiki.discoverydataservice.org/File:Fig%204.1.png|alt=|frameless|633x633px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example 4.1 Building the Medications Cohort SQL&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
-- step 1: cohort of the medication statement&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS cohort_medications_stmt;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_medications_stmt AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            m.id,&lt;br /&gt;
            m.non_core_concept_id,&lt;br /&gt;
            m.person_id,&lt;br /&gt;
            m.patient_id,&lt;br /&gt;
            m.clinical_effective_date,&lt;br /&gt;
            m.cancellation_date&lt;br /&gt;
     FROM medication_statement m&lt;br /&gt;
     JOIN cohort_patient cr&lt;br /&gt;
     ON m.organization_id = cr.organization_id&lt;br /&gt;
     AND m.person_id = cr.person_id&lt;br /&gt;
     AND m.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_stmt_ix ON cohort_medications_stmt(id);&lt;br /&gt;
&lt;br /&gt;
-- step 2: cohort of the medication oder&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS cohort_medications_ord;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_medications_ord AS  &lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            mo.id,&lt;br /&gt;
            mo.medication_statement_id&lt;br /&gt;
            mo.person_id,&lt;br /&gt;
            mo.patient_id,&lt;br /&gt;
            mo.clinical_effective_date&lt;br /&gt;
     FROM medication_order mo&lt;br /&gt;
     JOIN cohort_patient cr&lt;br /&gt;
     ON mo.organization_id = cr.organization_id&lt;br /&gt;
     AND mo.person_id = cr.person_id&lt;br /&gt;
     AND mo.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_ord_ix ON cohort_medications_ord(medication_statement_id);    &lt;br /&gt;
&lt;br /&gt;
-- step 3: cohort of the medication - statement plus order&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS cohort_medications;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_medications AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            ms.id,&lt;br /&gt;
            ms.non_core_concept_id,&lt;br /&gt;
            ms.person_id,&lt;br /&gt;
            ms.patient_id,&lt;br /&gt;
            mo.clinical_effective_date,&lt;br /&gt;
            ms.cancellation_date&lt;br /&gt;
      FROM cohort_medications_stmt ms JOIN cohort_medications_ord mo&lt;br /&gt;
      ON ms.id = mo.medication_statement_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_ix ON cohort_medications(non_core_concept_id);&lt;br /&gt;
CREATE INDEX med_pat_ix ON cohort_medications(patient_id);        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Building Datasets =&lt;br /&gt;
This involves creating a number of temporary tables in the database.&lt;br /&gt;
&lt;br /&gt;
===== Patient Demographic Dataset =====&lt;br /&gt;
A typical patient demographic dataset may consist of the following information:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Example_Patient_Demographic_Dataset.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Example%20Patient%20Demographic%20Dataset.png|alt=|frameless|1294x1294px]]&lt;br /&gt;
&lt;br /&gt;
For this example, we create the dataset to hold the patient demographic information as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE demographicsDataset (&lt;br /&gt;
  ExtractDate                    DATETIME     NULL,&lt;br /&gt;
  Pseudo_id                      VARCHAR(255) NULL,   -- unique identifier&lt;br /&gt;
  Pseudo_NHSNumber               VARCHAR(255) NULL,&lt;br /&gt;
  Gender                         VARCHAR(50)  NULL,&lt;br /&gt;
  Age                            VARCHAR(50)  NULL,&lt;br /&gt;
  DateOfBirth                    DATE         NULL,&lt;br /&gt;
  EthnicityLCode                 VARCHAR(50)  NULL,&lt;br /&gt;
  EthnicityLTerm                 VARCHAR(200) NULL,&lt;br /&gt;
  BirthCountryLCode              VARCHAR(50)  NULL,&lt;br /&gt;
  BirthCountryLTerm              VARCHAR(200) NULL,&lt;br /&gt;
  RegistrationStart              DATE         NULL,&lt;br /&gt;
  RegistrationEnd                DATE         NULL,&lt;br /&gt;
  IMD2010                        VARCHAR(50)  NULL,&lt;br /&gt;
  LSOA2011                       VARCHAR(50)  NULL,&lt;br /&gt;
  PracticeODSCode                VARCHAR(50)  NULL,&lt;br /&gt;
  PracticeODSName                VARCHAR(255) NULL,&lt;br /&gt;
  CCGName                        VARCHAR(100) NULL,&lt;br /&gt;
  YearOfDeath                    INT(4)       NULL,&lt;br /&gt;
  F2fVisits_Total                INT(11)      DEFAULT 0,&lt;br /&gt;
  F2fVisits_1year                INT(11)      DEFAULT 0,&lt;br /&gt;
  F2fVisits_5years               INT(11)      DEFAULT 0&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE demographicsDataset ADD INDEX demo_pseudoid_idx (pseudo_id);&lt;br /&gt;
&lt;br /&gt;
-- the unique identifier is usually the pseudo id&lt;br /&gt;
-- but to adapt for this schema we could use the person_id&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We pre-populate the dataset with the unique identifiers from the patient cohort:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
INSERT INTO demographicsDataset (pseudo_id, extractdate) SELECT DISTINCT group_by, now() FROM cohort_patient;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we run separate update SQL queries to populate the columns of the dataset table.&lt;br /&gt;
&lt;br /&gt;
For example, add registration start date, registration end date, practice ods code, practice ods name:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS reg_sort;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_reg;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_reg AS&lt;br /&gt;
SELECT c.group_by,&lt;br /&gt;
       e.date_regirstered,&lt;br /&gt;
       e.date_registered_end,&lt;br /&gt;
       o.name,&lt;br /&gt;
       o.ods_code&lt;br /&gt;
FROM cohort_patient c&lt;br /&gt;
JOIN episode_of_care e ON e.person_id = c.group_by&lt;br /&gt;
JOIN organization o ON o.id = e.organization_id&lt;br /&gt;
WHERE o.ods_code IN (&#039;EMIS99&#039;,&#039;5PC64&#039;); -- e.g. ods codes&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE reg_sort (&lt;br /&gt;
      row_id               INT,&lt;br /&gt;
      group_by             BIGINT,&lt;br /&gt;
      date_registered      DATE,&lt;br /&gt;
      date_regiostered_end DATE,&lt;br /&gt;
      ods_code             VARCHAR(50),&lt;br /&gt;
      name                 VARCHAR(255),&lt;br /&gt;
      rnk                  INT, PRIMARY KEY(row_id)) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
        a.group_by,&lt;br /&gt;
        a.date_registered,&lt;br /&gt;
        a.date_registered_end,&lt;br /&gt;
        a.ods_code,      &lt;br /&gt;
        a.name,&lt;br /&gt;
        a.rnk&lt;br /&gt;
FROM (SELECT q.group_by,&lt;br /&gt;
             q.date_registered,&lt;br /&gt;
             q.date_registered_end,&lt;br /&gt;
             q.name,&lt;br /&gt;
             q.ods_code,&lt;br /&gt;
             @currank := IF(@curperson = q.group_by, @currank + 1, 1) AS rnk,&lt;br /&gt;
             @curperson := q.group_by AS cur_person&lt;br /&gt;
      FROM qry_reg q, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
      ORDER BY q.group_by, q.date_registered DESC ) a,&lt;br /&gt;
      (SELECT @row_no := 0) t&lt;br /&gt;
WHERE a.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from reg_sort&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
       UPDATE demographicsDataset d&lt;br /&gt;
       JOIN reg_sort reg ON d.pseudo_id = reg.group_by&lt;br /&gt;
       SET d.RegistrationStart = reg.date_registered,&lt;br /&gt;
           d.RegistrationEnd = reg.date_registered_end,&lt;br /&gt;
           d.PracticeODSCode = reg.ods_code,&lt;br /&gt;
           d.PracticeODSName = reg.name&lt;br /&gt;
       WHERE reg.row_id &amp;gt; @row_id&lt;br /&gt;
       AND reg.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
       SET @row_id = @row_id + 1000;  &lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In this example, we created 2 temporary tables. The first to hold the registration data. The second to select from the first the records which contain the latest registration date.&lt;br /&gt;
&lt;br /&gt;
If the update is large in terms of the number of records to update then it might be possible to optimise the update process by using batches as demonstrated in the code example above.&lt;br /&gt;
&lt;br /&gt;
===== Patient Observations Dataset =====&lt;br /&gt;
A typical patient observations dataset may ask for the following information:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Example_Patient_Observations_dataset.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Example%20Patient%20Observations%20dataset.png|alt=|frameless|1122x1122px]]&lt;br /&gt;
&lt;br /&gt;
A typical dataset can have over 100 to 200 fields. Usually, there will be several datasets to deliver. But this illustrates the kind of layout we use for constructing an observational dataset.&lt;br /&gt;
&lt;br /&gt;
For this example, we create the dataset to hold the observational information as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS diagnoses2dataset;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE diagnoses2dataset (&lt;br /&gt;
  Pseudo_id                                        VARCHAR(255) NULL,&lt;br /&gt;
  Pseudo_NHSNumber                                 VARCHAR(255) NULL,&lt;br /&gt;
  AsthmaECode                                      VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaETerm                                      VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaEDate                                      VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeECode                                VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeETerm                                VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaEmergeEDate                                VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedECode                              VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedETerm                              VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaResolvedEDate                              VARCHAR(50) NULL,&lt;br /&gt;
  COPDECode                                        VARCHAR(50) NULL,&lt;br /&gt;
  COPDETerm                                        VARCHAR(200) NULL,&lt;br /&gt;
  COPDEDate                                        VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisECode                           VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisETerm                           VARCHAR(200) NULL,&lt;br /&gt;
  PulmonaryFibrosisEDate                           VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseECode                     VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseETerm                     VARCHAR(200) NULL,&lt;br /&gt;
  InterstitialLungDiseaseEDate                     VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationECode              VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationETerm              VARCHAR(200) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationEDate              VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaECode                                    VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaETerm                                    VARCHAR(200) NULL,&lt;br /&gt;
  GlaucomaEDate                                    VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisECode                         VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisETerm                         VARCHAR(200) NULL,&lt;br /&gt;
  RheumatoidArthritisEDate                         VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusECode                               VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusETerm                               VARCHAR(200) NULL,&lt;br /&gt;
  SystemicLupusEDate                               VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseECode                    VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseETerm                    VARCHAR(200) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseEDate                    VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseECode                               VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseETerm                               VARCHAR(200) NULL,&lt;br /&gt;
  CrohnsDiseaseEDate                               VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeECode                       VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeETerm                       VARCHAR(200) NULL,&lt;br /&gt;
  UlcerativeColitisCodeEDate                       VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisECode                            VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisETerm                            VARCHAR(200) NULL,&lt;br /&gt;
  AtopicDermatitisEDate                            VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceECode               VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceETerm               VARCHAR(200) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceEDate               VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaECode                    VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaETerm                    VARCHAR(200) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaEDate                    VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaECode                                    VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaETerm                                    VARCHAR(200) NULL,&lt;br /&gt;
  MelanomaEDate                                    VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerECode                              VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerETerm                              VARCHAR(200) NULL,&lt;br /&gt;
  ProstateCancerEDate                              VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerECode                                  VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerETerm                                  VARCHAR(200) NULL,&lt;br /&gt;
  LungCancerEDate                                  VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerECode                            VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerETerm                            VARCHAR(200) NULL,&lt;br /&gt;
  SmallBowelCancerEDate                            VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerECode                            VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerETerm                            VARCHAR(200) NULL,&lt;br /&gt;
  ColorectalCancerEDate                            VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerECode                                VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerETerm                                VARCHAR(200) NULL,&lt;br /&gt;
  BreastCancerEDate                                VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageECode                                 VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageETerm                                 VARCHAR(200) NULL,&lt;br /&gt;
  MiscarriageEDate                                 VARCHAR(50) NULL&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE diagnoses2dataset ADD INDEX pseudoid_idx (pseudo_id);&lt;br /&gt;
&lt;br /&gt;
INSERT INTO diagnoses2dataset (pseudo_id)&lt;br /&gt;
SELECT DISTINCT group_by FROM cohort_patient;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Each column of the dataset is named by joining pieces of identifiable information together by combining the observational type, the timeframe for the analysis and the requested field type.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 5.1 Shows the naming convention of a dataset column&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_5.1.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%205.1.png|alt=|frameless|744x744px]]&lt;br /&gt;
&lt;br /&gt;
Again, we pre-populate the dataset with the unique identifiers from the patient cohort.&lt;br /&gt;
&lt;br /&gt;
Then we run separate update SQL queries to populate the dataset table by calling multiple stored procedures.&lt;br /&gt;
&lt;br /&gt;
===== Patient Medications Dataset =====&lt;br /&gt;
A typical medication request would look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Medication_Request.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Medication%20Request.png|alt=|frameless|1235x1235px]]&lt;br /&gt;
&lt;br /&gt;
For this example, we create the dataset to hold the medication information as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE medications (&lt;br /&gt;
  pseudo_id            VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  nhsnumber            VARCHAR(10)  DEFAULT NULL,&lt;br /&gt;
  codedate             VARCHAR(20)  DEFAULT NULL,&lt;br /&gt;
  codeterm             VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  code                 VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codevalue            VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codeunit             VARCHAR(100) DEFAULT NULL&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Again, we pre-populate the dataset with the unique identifiers from the patient cohort.&lt;br /&gt;
&lt;br /&gt;
Then we run separate update SQL queries to populate the dataset table by calling multiple stored procedures.&lt;br /&gt;
&lt;br /&gt;
= Calling Stored Procedures =&lt;br /&gt;
Once we have generated the observation and medication datasets and cohorts, the next step is to populate the datasets using stored procedures.&lt;br /&gt;
&lt;br /&gt;
We construct a series of populate stored procedures which in turn call on other stored procedures to complete the update process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 6.1 Shows the datasets update process using stored procedures&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_6.1.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%206.1.png|alt=|frameless|590x590px]]&amp;lt;br /&amp;gt;&lt;br /&gt;
=== Calling the Populate Stored Procedures ===&lt;br /&gt;
Create a SQL script to call the stored procedure as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;StillbirthE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;237364002,161743003&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;PregnancyInducedHypertensionE&#039;,&#039;diagnoses2dataset&#039;,0,null,&#039;48194001&#039;,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;PreEclampsiaE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;398254007&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;CholestasisE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;235888006&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;GallstonesE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;266474003,407637009&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;GoutE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;90560007,161451004&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;AnkylosingSpondylitisE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;9631008&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;JaundiceE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;18165001,161536006&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;PsoriasisE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;9014002&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;DeafnessE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;15188001&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;HearingAidE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;365240006&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The procedure accepts a list of input parameters (this may vary depending on requirements):&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Parameter&lt;br /&gt;
|Data Type&lt;br /&gt;
|Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
|-&lt;br /&gt;
|col&lt;br /&gt;
|VARCHAR(100)&lt;br /&gt;
|the root of the column name&lt;br /&gt;
|-&lt;br /&gt;
|datasetTable&lt;br /&gt;
|VARCHAR(100)&lt;br /&gt;
|table name of dataset&lt;br /&gt;
|-&lt;br /&gt;
|reset&lt;br /&gt;
|BIT&lt;br /&gt;
|1 reset, 0 no reset of the dataset table&lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd1&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|include all snomed parents and their  children&lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd2&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|include snomed parents only&lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove3&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|exclude snomed parents and their children&lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove4&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|exclude only snomed parents&lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls&lt;br /&gt;
|VARCHAR(1)&lt;br /&gt;
|Y or N - whether to ignore null result  value&lt;br /&gt;
|}&lt;br /&gt;
A typical populate procedure is outlined below. (Please note this is only an example and may need modifications to it depending on your requirements).&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP PROCEDURE IF EXISTS populateCodeDate;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE populateCodeDate (&lt;br /&gt;
IN filterType     INT, -- 1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
IN col            VARCHAR(100), -- the root of the column name&lt;br /&gt;
IN datasetTable   VARCHAR(100), -- table name of dataset&lt;br /&gt;
IN reset          BIT, -- 1 reset, 0 no reset&lt;br /&gt;
IN codesToAdd1    VARCHAR(5000), -- all parents and their children&lt;br /&gt;
IN codesToAdd2    VARCHAR(5000), -- parents only&lt;br /&gt;
IN codesToRemove3 VARCHAR(5000), -- parents and their children to be excluded&lt;br /&gt;
IN codesToRemove4 VARCHAR(5000), -- just parents to be excluded&lt;br /&gt;
IN ignorenulls    VARCHAR(1)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
-- reset snomeds table&lt;br /&gt;
DELETE FROM snomeds WHERE cat_id IN (1, 2, 3, 4);&lt;br /&gt;
-- reset store table&lt;br /&gt;
DELETE FROM store WHERE id IN (1, 2, 3, 4);&lt;br /&gt;
&lt;br /&gt;
-- get snomed concept ids&lt;br /&gt;
&lt;br /&gt;
  IF codesToAdd1 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString(codesToAdd1, 1);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (1);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
  IF codesToAdd2 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString (codesToAdd2, 2);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (2);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
  IF codesToRemove3 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString (codesToRemove3, 3);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (3);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
  IF codesToRemove4 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString (codesToRemove4, 4);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (4);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
-- filter observation based on the snomed codes&lt;br /&gt;
&lt;br /&gt;
CALL filterObservations(filterType, 1, ignorenulls);&lt;br /&gt;
&lt;br /&gt;
-- reset columns&lt;br /&gt;
&lt;br /&gt;
IF (reset = 1) THEN&lt;br /&gt;
&lt;br /&gt;
  SET @reset_sql = CONCAT(&#039;UPDATE &#039;, datasetTable, &#039; SET &#039;,&lt;br /&gt;
  col, &amp;quot;Code = null, &amp;quot;,&lt;br /&gt;
  col, &amp;quot;Term = null, &amp;quot;,&lt;br /&gt;
  col, &amp;quot;Date = null&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  PREPARE resetStmt FROM @reset_sql;&lt;br /&gt;
  EXECUTE resetStmt;&lt;br /&gt;
  DEALLOCATE PREPARE resetStmt;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
  DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
  CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
      row_id                  INT,&lt;br /&gt;
      group_by                VARCHAR(255),&lt;br /&gt;
      original_code           VARCHAR(20),&lt;br /&gt;
      original_term           VARCHAR(200),&lt;br /&gt;
      clinical_effective_date DATE, PRIMARY KEY(row_id)&lt;br /&gt;
  ) AS&lt;br /&gt;
&lt;br /&gt;
  SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
         f.group_by,&lt;br /&gt;
         f.original_code,&lt;br /&gt;
         f.original_term,&lt;br /&gt;
         f.clinical_effective_date&lt;br /&gt;
  FROM filteredObservations f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
             &#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
             col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
             col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
             col, &amp;quot;Date = date_format(f.clinical_effective_date, &#039;%d/%m/%Y&#039;)&lt;br /&gt;
             WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
  PREPARE stmt FROM @sql;&lt;br /&gt;
  EXECUTE stmt;&lt;br /&gt;
  DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Retrieving the SNOMED Concept Ids ====&lt;br /&gt;
The procedure accepts a list of SNOMED codes as input parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 6.2 Shows how the SNOMED codes are being retrieved using stored procedures&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_6.2.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%206.2.png|alt=|frameless|818x818px]]&amp;lt;br /&amp;gt;&lt;br /&gt;
===== Calling the storeSnomedString procedure =====&lt;br /&gt;
We call the storeSnomedString procedure to separate the list of SNOMED codes into their individual values and store them into a temporary table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS store;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE store (&lt;br /&gt;
  id            INT,&lt;br /&gt;
  org_snomed_id BIGINT&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE store ADD INDEX store_idx (org_snomed_id);&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS storeSnomedString;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE PROCEDURE storeSnomedString (&lt;br /&gt;
  IN stringValue VARCHAR(5000),&lt;br /&gt;
  IN cat_id      INT&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
  DECLARE front       VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
  DECLARE frontlen    INT           DEFAULT NULL;&lt;br /&gt;
  DECLARE TempValue   VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
&lt;br /&gt;
  processloop:&lt;br /&gt;
  LOOP&lt;br /&gt;
&lt;br /&gt;
      IF LENGTH(TRIM(stringValue)) = 0&lt;br /&gt;
      OR stringValue IS NULL THEN&lt;br /&gt;
        LEAVE process loop;&lt;br /&gt;
      END IF;&lt;br /&gt;
   &lt;br /&gt;
  SET front = SUBSTRING_INDEX(stringValue, &#039;,&#039;, 1);&lt;br /&gt;
  SET frontlen = LENGTH(front);&lt;br /&gt;
  SET TempValue = TRIM(front);&lt;br /&gt;
&lt;br /&gt;
  INSERT INTO store (id, org_snomed_id)&lt;br /&gt;
  VALUES (cat_id, CAST(TempValue AS SIGNED));&lt;br /&gt;
&lt;br /&gt;
  SET stringValue = INSERT(stringValue, 1, frontlen + 1, &#039;&#039;);&lt;br /&gt;
&lt;br /&gt;
  END LOOP;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Calling the getAllSnomedsConceptIds procedure =====&lt;br /&gt;
We call another procedure getAllSnomedsConceptIdsFromSnomedCodes to loop through the temporary table. For each SNOMED code, we retrieve the corresponding SNOMED concept id.&lt;br /&gt;
&lt;br /&gt;
We store the resultant set of concept ids into another temporary table where it will be used to query observational data.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS snomeds;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE snomeds (&lt;br /&gt;
  cat_id               INT,&lt;br /&gt;
  snomed_concept_id    BIGINT,&lt;br /&gt;
  core_concept         INT,&lt;br /&gt;
  snomed_code          BIGINT,&lt;br /&gt;
  term                 VARCHAR(255)&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX sno_cpt_idx(snomed_concept_id);&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX sno_code_idx(snomed_code);&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS getAllSnomedsConceptIds;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE getAllSnomedsConceptIds(p_cat_id INT)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
  DECLARE done           INT;&lt;br /&gt;
  DECLARE l_parent_id    BIGINT;&lt;br /&gt;
  DECLARE c_get_snomeds  CURSOR FOR SELECT org_snomed_id&lt;br /&gt;
                         FROM store WHERE id = p_cat_id;&lt;br /&gt;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;&lt;br /&gt;
&lt;br /&gt;
  SET done = 0;&lt;br /&gt;
&lt;br /&gt;
       OPEN c_get_snomeds;&lt;br /&gt;
&lt;br /&gt;
       processloop:&lt;br /&gt;
       WHILE (done = 0) DO&lt;br /&gt;
&lt;br /&gt;
        FETCH c_get_snomeds INTO l_parent_id;&lt;br /&gt;
&lt;br /&gt;
        IF done = 1 THEN&lt;br /&gt;
           LEAVE processloop;&lt;br /&gt;
        END IF;&lt;br /&gt;
&lt;br /&gt;
        IF p_cat_id IN (1, 3) THEN&lt;br /&gt;
&lt;br /&gt;
          -- get parent snomed concept id based on the snomed code&lt;br /&gt;
&lt;br /&gt;
          INSERT INTO snomeds&lt;br /&gt;
          SELECT&lt;br /&gt;
                p_cat_id,&lt;br /&gt;
                cm.legacy,&lt;br /&gt;
                cm.core,&lt;br /&gt;
                c.code,&lt;br /&gt;
                c.name&lt;br /&gt;
           FROM concept_map cm JOIN concept c ON c.dbid = cm.core&lt;br /&gt;
           WHERE c.code = l_parent_id;&lt;br /&gt;
&lt;br /&gt;
           -- get all child snomed concept ids based on the parent snomed code&lt;br /&gt;
&lt;br /&gt;
           INSERT INTO snomeds&lt;br /&gt;
           SELECT&lt;br /&gt;
                 p_cat_id,&lt;br /&gt;
                 cptm.legacy,&lt;br /&gt;
                 cptm.core,&lt;br /&gt;
                 cpt.code,&lt;br /&gt;
                 cpt.name&lt;br /&gt;
            FROM concept_tct tc&lt;br /&gt;
            JOIN snomeds sn ON sn.core_concept = tc.target&lt;br /&gt;
            AND tc.property = &#039;92842&#039;&lt;br /&gt;
            JOIN concept_map cptm on cptm.core = tc.source&lt;br /&gt;
            AND tc.property = &#039;92842&#039;&lt;br /&gt;
            JOIN concept cpt on cpt.dbid = cptm.core&lt;br /&gt;
            WHERE sn.snomed_code = l_parent_id;&lt;br /&gt;
&lt;br /&gt;
        ELSE&lt;br /&gt;
&lt;br /&gt;
        -- include or exclude parent snomed&lt;br /&gt;
&lt;br /&gt;
            INSERT INTO snomeds&lt;br /&gt;
            SELECT&lt;br /&gt;
                  p_cat_id,&lt;br /&gt;
                  cm.legacy,&lt;br /&gt;
                  cm.core,&lt;br /&gt;
                  c.code,&lt;br /&gt;
                  c.name&lt;br /&gt;
             FROM concept_map cm JOIN concept c ON c.dbid = cm.core&lt;br /&gt;
             WHERE c.code = l_parent_id;&lt;br /&gt;
&lt;br /&gt;
        END IF;&lt;br /&gt;
&lt;br /&gt;
       END WHILE processloop;&lt;br /&gt;
       CLOSE c_get_snomeds;&lt;br /&gt;
&lt;br /&gt;
       SET done = 0;  &lt;br /&gt;
&lt;br /&gt;
END //&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Using the concept_tct table, which is a transitive closure table, we are able to find the corresponding child concept ids based on the parent concept id.&lt;br /&gt;
&lt;br /&gt;
Match the parent against the value in the target column. This will return all the children in the source column.&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Concepts_tct.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Concepts%20tct.png|alt=|frameless]]&lt;br /&gt;
&lt;br /&gt;
==== Retrieving filtered Observations ====&lt;br /&gt;
Using the prepared observations cohort, the next step is to filter out this cohort based on the SNOMED code list and the timeframe, which have been passed in as input parameters from the populate procedure.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 6.3 Shows the processing of the FilterObservations procedure&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_6.3.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%206.3.png|alt=|frameless|596x596px]]&amp;lt;br /&amp;gt;&lt;br /&gt;
===== Calling the filterObservations procedure =====&lt;br /&gt;
By calling the filterObservations procedure, we pass in the following input parameters:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Parameter&lt;br /&gt;
|Data Type&lt;br /&gt;
|Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|Timeframe of the analysis. 0 = earliest, 1  = latest, 2 = ever, 3 = pivot around cohort pivot date (6 months), 4 = All  since&lt;br /&gt;
|-&lt;br /&gt;
|toCreate&lt;br /&gt;
|INT&lt;br /&gt;
|1 is the default value - to call  createObservationsFromCohort&lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls&lt;br /&gt;
|VARCHAR(1)&lt;br /&gt;
|Y or N - whether to ignore null result  value&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP PROCEDURE IF EXISTS filterObservations;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE PROCEDURE filterObservations (&lt;br /&gt;
     IN filterType INT,&lt;br /&gt;
-- 0 earliest, 1 latest, 2 ever,&lt;br /&gt;
-- 3 pivot around cohort pivot date (6 months),&lt;br /&gt;
-- 4 allSince&lt;br /&gt;
  IN toCreate   INT, -- 1 to createObservationsFromCohortv2&lt;br /&gt;
  IN ignorenulls VARCHAR(1) -- Y or N&lt;br /&gt;
  )&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
IF (toCreate = 1) THEN&lt;br /&gt;
  CALL createObservationsFromCohort (filterType);&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS filteredObservations;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 0) THEN -- earliest&lt;br /&gt;
&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN  &lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT&lt;br /&gt;
          ob.id,&lt;br /&gt;
          ob.group_by,&lt;br /&gt;
          ob.patient_id,&lt;br /&gt;
          ob.person_id,&lt;br /&gt;
          ob.original_code,&lt;br /&gt;
          ob.original_term,&lt;br /&gt;
          ob.result_value,&lt;br /&gt;
          ob.clinical_effective_date,&lt;br /&gt;
          ob.result_value_units,&lt;br /&gt;
          ob.age_years,&lt;br /&gt;
          ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.original_code,&lt;br /&gt;
            o.original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years,&lt;br /&gt;
            @currank :=&lt;br /&gt;
                 IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
            @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          WHERE o.result_value IS NOT NULL&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date ASC,&lt;br /&gt;
                   o.id ASC -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
  ELSE&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT&lt;br /&gt;
          ob.id,&lt;br /&gt;
          ob.group_by,&lt;br /&gt;
          ob.patient_id,&lt;br /&gt;
          ob.person_id,&lt;br /&gt;
          ob.original_code,&lt;br /&gt;
          ob.original_term,&lt;br /&gt;
          ob.result_value,&lt;br /&gt;
          ob.clinical_effective_date,&lt;br /&gt;
          ob.result_value_units,&lt;br /&gt;
          ob.age_years,&lt;br /&gt;
          ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.original_code,&lt;br /&gt;
            o.original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years,&lt;br /&gt;
            @currank :=&lt;br /&gt;
                  IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
            @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date ASC,&lt;br /&gt;
                   o.id ASC -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
ELSEIF (filterType = 2 OR filterType = 4) THEN -- ever or allSince&lt;br /&gt;
&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
          mc.group_by,&lt;br /&gt;
          mc.patient_id,&lt;br /&gt;
          mc.person_id,&lt;br /&gt;
          mc.original_code,&lt;br /&gt;
          mc.original_term,&lt;br /&gt;
          mc.result_value,&lt;br /&gt;
          mc.clinical_effective_date,&lt;br /&gt;
          mc.result_value_units,&lt;br /&gt;
          mc.age_years&lt;br /&gt;
  FROM observationsFromCohort mc&lt;br /&gt;
  WHERE mc.result_value IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
  ELSE&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
          mc.group_by,&lt;br /&gt;
          mc.patient_id,&lt;br /&gt;
          mc.person_id,&lt;br /&gt;
          mc.original_code,&lt;br /&gt;
          mc.original_term,&lt;br /&gt;
          mc.result_value,&lt;br /&gt;
          mc.clinical_effective_date,&lt;br /&gt;
          mc.result_value_units,&lt;br /&gt;
          mc.age_years&lt;br /&gt;
  FROM observationsFromCohort mc;&lt;br /&gt;
&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ELSEIF (filterType = 1 or filterType = 3) THEN -- latest or pivot&lt;br /&gt;
&lt;br /&gt;
IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE filteredObservations as&lt;br /&gt;
  SELECT&lt;br /&gt;
           ob.id,&lt;br /&gt;
           ob.group_by,&lt;br /&gt;
           ob.patient_id,&lt;br /&gt;
           ob.person_id,&lt;br /&gt;
           ob.original_code,&lt;br /&gt;
           ob.original_term,&lt;br /&gt;
           ob.result_value,&lt;br /&gt;
           ob.clinical_effective_date,&lt;br /&gt;
           ob.result_value_units,&lt;br /&gt;
           ob.age_years,&lt;br /&gt;
           ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.original_code,&lt;br /&gt;
            o.original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years,&lt;br /&gt;
            @currank :=&lt;br /&gt;
                IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
            @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          WHERE o.result_value IS NOT NULL&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date DESC,&lt;br /&gt;
                   o.id DESC -- latest&lt;br /&gt;
        ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservationsV2 as&lt;br /&gt;
  SELECT&lt;br /&gt;
           ob.id,&lt;br /&gt;
           ob.group_by,&lt;br /&gt;
           ob.patient_id,&lt;br /&gt;
           ob.person_id,&lt;br /&gt;
           ob.original_code,&lt;br /&gt;
           ob.original_term,&lt;br /&gt;
           ob.result_value,&lt;br /&gt;
           ob.clinical_effective_date,&lt;br /&gt;
           ob.result_value_units,&lt;br /&gt;
           ob.age_years,&lt;br /&gt;
           ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
             o.id,&lt;br /&gt;
             o.group_by,&lt;br /&gt;
             o.patient_id,&lt;br /&gt;
             o.person_id,&lt;br /&gt;
             o.original_code,&lt;br /&gt;
             o.original_term,&lt;br /&gt;
             o.result_value,&lt;br /&gt;
             o.clinical_effective_date,&lt;br /&gt;
             o.result_value_units,&lt;br /&gt;
             o.age_years,&lt;br /&gt;
             @currank :=&lt;br /&gt;
                  IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
             @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date DESC,&lt;br /&gt;
                   o.id DESC -- latest&lt;br /&gt;
         ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
&lt;br /&gt;
  SIGNAL SQLSTATE &#039;45000&#039;&lt;br /&gt;
&lt;br /&gt;
  SET MESSAGE_TEXT = &#039;filterType not recognised&#039;;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Calling the createObservationsFromCohort procedure =====&lt;br /&gt;
From the filterObservations procedure, we call the createobservationsFromCohort procedure, passing in the filter type as the input parameter:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL createObservationsFromCohort (filterType);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP PROCEDURE IF EXISTS createObservationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE PROCEDURE createObservationsFromCohort (&lt;br /&gt;
  IN filterType INT&lt;br /&gt;
  -- 0 earliest, 1 latest, 2 ever,&lt;br /&gt;
  -- 3 pivot around cohort_patient pivot date (6 months)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS snomeds_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE snomeds_tmp AS&lt;br /&gt;
SELECT cat_id,&lt;br /&gt;
      snomed_concept_id&lt;br /&gt;
FROM snomeds&lt;br /&gt;
WHERE cat_id IN (3, 4);&lt;br /&gt;
&lt;br /&gt;
-- delete from snomeds all snomed_concept ids to be excluded&lt;br /&gt;
&lt;br /&gt;
DELETE t1 FROM snomeds t1 JOIN snomeds_tmp t2&lt;br /&gt;
ON t1.snomed_concept_id = t2.snomed_concept_id&lt;br /&gt;
WHERE t1.cat_id IN (1, 2);&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS observationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 3) THEN&lt;br /&gt;
&lt;br /&gt;
-- pivot over 6 months from pivot date (already set in cohort)&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE observationsFromCohort AS&lt;br /&gt;
      SELECT DISTINCT&lt;br /&gt;
             o.id,&lt;br /&gt;
             o.patient_id,&lt;br /&gt;
             o.person_id,&lt;br /&gt;
             o.group_by,&lt;br /&gt;
             o.clinical_effective_date,&lt;br /&gt;
             o.non_core_concept_id AS original_code,&lt;br /&gt;
             SUBSTRING(s.term, 1, 200) AS original_term,&lt;br /&gt;
             o.result_value,&lt;br /&gt;
             o.result_value_units,&lt;br /&gt;
             o.age_years&lt;br /&gt;
     FROM cohort_observations o&lt;br /&gt;
          JOIN snomeds s ON s.snomed_concept_id = o.non_core_concept_id&lt;br /&gt;
     WHERE o.clinical_effective_date IS NOT NULL&lt;br /&gt;
     AND o.clinical_effective_date&lt;br /&gt;
     BETWEEN DATE_SUB(c.pivot_date, INTERVAL 6 MONTH)&lt;br /&gt;
     AND DATE_SUB(c.pivot_date, INTERVAL -6 MONTH);&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
&lt;br /&gt;
-- latest or earliest or ever (so all observations)&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE observationsFromCohort AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.non_core_concept_id AS original_code,&lt;br /&gt;
            SUBSTRING(s.term, 1, 200) AS original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years&lt;br /&gt;
     FROM cohort_observations o&lt;br /&gt;
          JOIN snomeds s ON s.snomed_concept_id = o.non_core_concept_id&lt;br /&gt;
     WHERE o.clinical_effective_date IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE observationsFromCohort ADD INDEX obv_pat_idx(patient_id);&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;This returns a new observations cohort based on the SNOMED code list.&lt;br /&gt;
&lt;br /&gt;
===== Filter Observations by Timeframe =====&lt;br /&gt;
Using the new observations cohort, we pass in the filter type to retrieve the observations based on the timeframe for the analysis, and using the clinical effective date to determine the time intervals. We store the result set into a temporary table. This is illustrated in the code snippet of the filterObservations procedure below: &amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE filteredObservationsV2 as&lt;br /&gt;
  SELECT&lt;br /&gt;
          ob.id,&lt;br /&gt;
          ob.group_by,&lt;br /&gt;
          ob.patient_id,&lt;br /&gt;
          ob.person_id,&lt;br /&gt;
          ob.original_code,&lt;br /&gt;
          ob.original_term,&lt;br /&gt;
          ob.result_value,&lt;br /&gt;
          ob.clinical_effective_date,&lt;br /&gt;
          ob.result_value_units,&lt;br /&gt;
          ob.age_years,&lt;br /&gt;
          ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT o.id,&lt;br /&gt;
                 o.group_by,&lt;br /&gt;
                 o.patient_id,&lt;br /&gt;
                 o.person_id,&lt;br /&gt;
                 o.original_code,&lt;br /&gt;
                 o.original_term,&lt;br /&gt;
                 o.result_value,&lt;br /&gt;
                 o.clinical_effective_date,&lt;br /&gt;
                 o.result_value_units,&lt;br /&gt;
                 o.age_years,&lt;br /&gt;
                 @currank :=&lt;br /&gt;
                    IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
                 @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          WHERE o.result_value IS NOT NULL&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date ASC,&lt;br /&gt;
                   o.id ASC  -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Updating Dataset Table Columns ====&lt;br /&gt;
Using the result set derived from the above step, we can now update the dataset table columns with the appropriate observational values. This is illustrated in the code snippet of the populateCodeDate procedure:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
  CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
      row_id                  INT,&lt;br /&gt;
      group_by                VARCHAR(255),&lt;br /&gt;
      original_code           VARCHAR(20),&lt;br /&gt;
      original_term           VARCHAR(200),&lt;br /&gt;
      clinical_effective_date DATE, PRIMARY KEY(row_id)&lt;br /&gt;
  ) AS&lt;br /&gt;
  SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
         f.group_by,&lt;br /&gt;
         f.original_code,&lt;br /&gt;
         f.original_term,&lt;br /&gt;
         f.clinical_effective_date&lt;br /&gt;
  FROM filteredObservationsV2 f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
              &#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
              col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
              col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
              col, &amp;quot;Date = date_format(f.clinical_effective_date, &#039;%d/%m/%Y&#039;)&lt;br /&gt;
              WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
  PREPARE stmt FROM @sql;&lt;br /&gt;
  EXECUTE stmt;&lt;br /&gt;
  DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;To populate medication datasets, we use a similar approach as above.&lt;br /&gt;
&lt;br /&gt;
= APPENDIX =&lt;br /&gt;
Some supplementary material that maybe helpful when developing SQL query, in particular, working with concept ids.&lt;br /&gt;
&lt;br /&gt;
=== Helper Functions ===&lt;br /&gt;
•          Function to retrieve the description field on the concept table based on the concept id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_concept_desc(p_concept_id int)&lt;br /&gt;
RETURNS VARCHAR(400)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_description VARCHAR(400);&lt;br /&gt;
&lt;br /&gt;
SELECT description INTO l_description&lt;br /&gt;
FROM concept cpt&lt;br /&gt;
WHERE cpt.dbid = p_concept_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_description;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_concept_desc(pat.gender_concept_id) FROM patient pat;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the code field on the concept table based on the concept id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_concept_code;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_concept_code(p_concept_id int)&lt;br /&gt;
RETURNS VARCHAR(20)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_code VARCHAR(20);&lt;br /&gt;
&lt;br /&gt;
SELECT code INTO l_code&lt;br /&gt;
FROM concept cpt&lt;br /&gt;
WHERE cpt.dbid = p_concept_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_code;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_concept_code(epoc.registration_type_concept_id)&lt;br /&gt;
       FROM episode_of_care epoc;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the scheme field on the concept table based on the concept id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_concept_scheme;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_concept_scheme(p_concept_id int)&lt;br /&gt;
RETURNS VARCHAR(50)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_scheme VARCHAR(50);&lt;br /&gt;
&lt;br /&gt;
SELECT scheme INTO l_scheme&lt;br /&gt;
FROM concept cpt&lt;br /&gt;
WHERE cpt.dbid = p_concept_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_scheme;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_concept_scheme(ob.non_core_concept_id) FROM observation ob;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the ods code based on the organization id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_ods;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_ods(p_org_id bigint)&lt;br /&gt;
RETURNS VARCHAR(50)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_ods_code VARCHAR(50);&lt;br /&gt;
&lt;br /&gt;
SELECT ods_code INTO l_ods_code&lt;br /&gt;
FROM organization org&lt;br /&gt;
WHERE org.id = p_org_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_ods_code;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_ods(pat.registered_practice_organization_id) FROM patient pat;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the organization name based on the organization id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_org_name;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_org_name(p_org_id bigint)&lt;br /&gt;
RETURNS VARCHAR(255)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_org_name VARCHAR(255);&lt;br /&gt;
&lt;br /&gt;
SELECT name INTO l_org_name&lt;br /&gt;
FROM organization org&lt;br /&gt;
WHERE org.id = p_org_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_org_name;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_ods(pat.registered_practice_organization_id) FROM patient pat;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the event type from the description of the concept table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_event_type;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_event_type(p_description VARCHAR(400))&lt;br /&gt;
RETURNS VARCHAR(50)&lt;br /&gt;
NOT DETERMINISTIC NO SQL&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_event VARCHAR(50);&lt;br /&gt;
&lt;br /&gt;
SELECT substr(p_description,instr(p_description,&amp;quot;(&amp;quot;) + 1,&lt;br /&gt;
       instr(p_description,&amp;quot;)&amp;quot;) - instr(p_description,&amp;quot;(&amp;quot;) - 1)&lt;br /&gt;
       INTO l_event;&lt;br /&gt;
&lt;br /&gt;
RETURN l_event;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_event_type(get_concept_desc(ob.core_concept_id)) event_type&lt;br /&gt;
       FROM observation ob;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the latest medication order issue date based on the medication statement id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_lastest_med_issue_date;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_lastest_med_issue_date (p_medication_stmt_id bigint)&lt;br /&gt;
RETURNS DATE&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_date DATE;&lt;br /&gt;
DECLARE l_rnk  INT;&lt;br /&gt;
&lt;br /&gt;
-- get latest issue date&lt;br /&gt;
&lt;br /&gt;
  SELECT&lt;br /&gt;
          med.clinical_effective_date, med.rnk INTO l_date, l_rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            mo.id,&lt;br /&gt;
            mo.medication_statement_id,&lt;br /&gt;
            mo.clinical_effective_date,&lt;br /&gt;
            @currank :=&lt;br /&gt;
            IF(@curmedstmt = mo.medication_statement_id,@currank + 1,1) AS rnk,&lt;br /&gt;
            @curmedstmt := mo.medication_statement_id AS cur_med_stmt&lt;br /&gt;
          FROM medication_order mo,(SELECT @currank := 0, @curmedstmt := 0) r&lt;br /&gt;
          WHERE mo.medication_statement_id = p_medication_stmt_id&lt;br /&gt;
          ORDER BY mo.medication_statement_id DESC,&lt;br /&gt;
                   mo.clinical_effective_date DESC,&lt;br /&gt;
                   mo.id DESC -- latest&lt;br /&gt;
        ) med&lt;br /&gt;
  WHERE med.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
RETURN l_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT DATE_FORMAT(get_lastest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) date_of_last_issue&lt;br /&gt;
       FROM medication_statement med;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the earliest medication order issue date based on the medication statement id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_earliest_med_issue_date;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_earliest_med_issue_date (p_medication_stmt_id bigint)&lt;br /&gt;
RETURNS DATE&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_date DATE;&lt;br /&gt;
DECLARE l_rnk  INT;&lt;br /&gt;
&lt;br /&gt;
-- get earliest issue date&lt;br /&gt;
&lt;br /&gt;
  SELECT&lt;br /&gt;
          med.clinical_effective_date, med.rnk INTO l_date, l_rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            mo.id,&lt;br /&gt;
            mo.medication_statement_id,&lt;br /&gt;
            mo.clinical_effective_date,&lt;br /&gt;
            @currank :=&lt;br /&gt;
            IF(@curmedstmt = mo.medication_statement_id,@currank + 1,1) AS rnk,&lt;br /&gt;
            @curmedstmt := mo.medication_statement_id AS cur_med_stmt&lt;br /&gt;
          FROM medication_order mo,(SELECT @currank := 0, @curmedstmt := 0) r&lt;br /&gt;
          WHERE mo.medication_statement_id = p_medication_stmt_id&lt;br /&gt;
          ORDER BY mo.medication_statement_id ASC,&lt;br /&gt;
                   mo.clinical_effective_date ASC,&lt;br /&gt;
                   mo.id ASC -- earliest&lt;br /&gt;
        ) med&lt;br /&gt;
  WHERE med.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
RETURN l_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT DATE_FORMAT(get_earliest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) issue_date&lt;br /&gt;
       FROM medication_statement med;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the event log last changed date based on the record id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_event_maxdate;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_event_maxdate (p_table_id tinyint, p_record_id bigint)&lt;br /&gt;
RETURNS DATETIME&lt;br /&gt;
READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_max_date DATETIME;&lt;br /&gt;
&lt;br /&gt;
SELECT  MAX(etlg.dt_change) modified_date INTO l_max_date&lt;br /&gt;
FROM event_log etlg&lt;br /&gt;
WHERE etlg.table_id = p_table_id AND record_id = p_record_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_max_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_event_maxdate(2,pat.id) FROM patient pat;&lt;br /&gt;
       Where 2 is the table id for the patient table.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the event log last changed date for medication order based on the medication statement id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_event_maxdate_medo;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_event_maxdate_medo (p_table_id tinyint, p_record_id bigint)&lt;br /&gt;
RETURNS datetime&lt;br /&gt;
READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_max_date datetime;&lt;br /&gt;
&lt;br /&gt;
SELECT MAX(etlg.dt_change) INTO l_max_date&lt;br /&gt;
FROM event_log etlg JOIN medication_order mo&lt;br /&gt;
ON mo.medication_statement_id = p_record_id&lt;br /&gt;
AND mo.id = etlg.record_id&lt;br /&gt;
WHERE etlg.table_id = p_table_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_max_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_event_maxdate_medo (9,med.id) FROM medication_statement med &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQL Statement Examples ===&lt;br /&gt;
&#039;&#039;&#039;Patient Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     pat.id patient_id,&lt;br /&gt;
     get_ods(pat.registered_practice_organization_id) source_organization,&lt;br /&gt;
     IF (pat.date_of_death IS NULL,&lt;br /&gt;
        TIMESTAMPDIFF(YEAR,pat.date_of_birth,CURDATE()),&lt;br /&gt;
        TIMESTAMPDIFF(YEAR,pat.date_of_birth,pat.date_of_death)) age,&lt;br /&gt;
     get_concept_desc(pat.gender_concept_id) sex,&lt;br /&gt;
     DATE_FORMAT(epoc.startregdate,&amp;quot;%Y%m%d&amp;quot;) start_regdate,&lt;br /&gt;
     get_concept_code(epoc.registration_type_concept_id)  reg_status,&lt;br /&gt;
     pat.nhs_number nhsnumber,&lt;br /&gt;
     pad.postcode,&lt;br /&gt;
     DATE_FORMAT(pat.date_of_birth,&amp;quot;%Y%m%d&amp;quot;) date_of_birth,&lt;br /&gt;
     epoc.usualgp,&lt;br /&gt;
     pat.first_names forename,&lt;br /&gt;
     pat.last_name surname,&lt;br /&gt;
     get_concept_desc(pat.ethnic_code_concept_id) ethnic_category,&lt;br /&gt;
     DATE_FORMAT(pat.date_of_death,&amp;quot;%Y%m%d&amp;quot;) date_of_death,&lt;br /&gt;
     DATE_FORMAT(epoc.endregdate,&amp;quot;%Y%m%d&amp;quot;) end_reg_date,&lt;br /&gt;
     get_optout_status(pat.id, pat.registered_practice_organization_id) optout_status,&lt;br /&gt;
     pad.address_line_1,&lt;br /&gt;
     pad.address_line_2,&lt;br /&gt;
     pad.address_line_3,&lt;br /&gt;
     pad.address_line_4,&lt;br /&gt;
     pad.city,&lt;br /&gt;
     NULLIF(GREATEST(COALESCE(get_event_maxdate(2,pat.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate(20,pad.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate(6,epoc.id),0)),0) last_modified_date&lt;br /&gt;
FROM patient pat&lt;br /&gt;
LEFT JOIN patient_address pad ON pat.current_address_id = pad.id AND pat.id = pad.patient_id&lt;br /&gt;
LEFT JOIN&lt;br /&gt;
  (SELECT&lt;br /&gt;
          epc.id,&lt;br /&gt;
          epc.organization_id,&lt;br /&gt;
          epc.patient_id,&lt;br /&gt;
          epc.registration_type_concept_id,&lt;br /&gt;
          epc.registration_status_concept_id,&lt;br /&gt;
          epc.date_registered startregdate,&lt;br /&gt;
          epc.date_registered_end endregdate,&lt;br /&gt;
          epc.usual_gp_practitioner_id,&lt;br /&gt;
          prac.name usualgp&lt;br /&gt;
  FROM episode_of_care epc LEFT JOIN practitioner prac&lt;br /&gt;
  ON prac.id = epc.usual_gp_practitioner_id&lt;br /&gt;
  AND prac.organization_id = epc.organization_id) epoc&lt;br /&gt;
  ON epoc.patient_id = pat.id&lt;br /&gt;
  AND epoc.organization_id = pat.registered_practice_organization_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Prescription Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     med.id event_id,&lt;br /&gt;
     get_ods(med.organization_id) source_organization,&lt;br /&gt;
     med.patient_id patient_id,&lt;br /&gt;
     pat.nhs_number nhs_number,&lt;br /&gt;
     get_concept_desc(med.authorisation_type_concept_id)  issue_type,&lt;br /&gt;
     DATE_FORMAT(get_earliest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) issue_date,&lt;br /&gt;
     DATE_FORMAT(get_lastest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) date_of_last_issue,&lt;br /&gt;
     DATE_FORMAT(med.clinical_effective_date,&amp;quot;%Y%m%d&amp;quot;) recorded_date,&lt;br /&gt;
     get_concept_code(med.core_concept_id) dmdcode,&lt;br /&gt;
     med.dose dosage,&lt;br /&gt;
     med.quantity_value quantity,&lt;br /&gt;
     med.quantity_unit quantity_unit,&lt;br /&gt;
     get_concept_desc(med.core_concept_id) dmd_description,&lt;br /&gt;
     NULLIF(GREATEST(COALESCE(get_event_maxdate(10,med.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate_medo(9,med.id),0)),0) last_modified_date&lt;br /&gt;
FROM medication_statement med JOIN patient pat ON med.patient_id = pat.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Clinical Event Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     ob.id event_id,&lt;br /&gt;
     get_ods(ob.organization_id) source_organization,&lt;br /&gt;
     ob.patient_id,&lt;br /&gt;
     pat.nhs_number nhs_number,&lt;br /&gt;
     DATE_FORMAT(ob.clinical_effective_date,&amp;quot;%Y%m%d&amp;quot;) effective_date,&lt;br /&gt;
     get_concept_code(ob.non_core_concept_id) original_code,&lt;br /&gt;
     get_concept_desc(ob.non_core_concept_id) original_term,&lt;br /&gt;
     get_concept_desc(get_concept_scheme(ob.non_core_concept_id)) original_code_scheme,&lt;br /&gt;
     ob.result_value result_value,&lt;br /&gt;
     CASE&lt;br /&gt;
     WHEN ob.is_problem = 1 AND ob.is_review = 0 THEN &#039;N - New&#039;&lt;br /&gt;
     WHEN ob.is_problem = 1 AND ob.is_review = 1 THEN &#039;F - Follow Up&#039;&lt;br /&gt;
     ELSE &#039;O - Other&#039;&lt;br /&gt;
     END AS episode_type,&lt;br /&gt;
     prac.role_desc hcp_role,&lt;br /&gt;
     enc.type encounter_type,&lt;br /&gt;
     get_concept_code(ob.core_concept_id) snomed_concept_id,&lt;br /&gt;
     get_concept_desc(ob.core_concept_id) snomed_term,&lt;br /&gt;
     ob.parent_observation_id parent_event,&lt;br /&gt;
     get_ods(enc.organization_id) event_location,&lt;br /&gt;
     get_org_name(enc.organization_id) location_name,&lt;br /&gt;
     enc.appointment_id appointment_slot_id,&lt;br /&gt;
     get_event_type(get_concept_desc(ob.core_concept_id)) event_type,&lt;br /&gt;
    NULLIF(GREATEST(COALESCE(get_event_maxdate(11,ob.id),0),&lt;br /&gt;
                    COALESCE(get_event_maxdate(5,enc.id),0),&lt;br /&gt;
                    COALESCE(get_event_maxdate(13,prac.id),0)),0) last_modified_date&lt;br /&gt;
FROM observation ob JOIN patient pat ON ob.patient_id = pat.id&lt;br /&gt;
LEFT JOIN practitioner prac ON prac.id = ob.practitioner_id&lt;br /&gt;
LEFT JOIN encounter enc ON enc.id = ob.encounter_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Patient Registration History Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     get_ods(epoc.organization_id) source_origanization,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     get_concept_code(epoc.registration_status_concept_id) reg_status,&lt;br /&gt;
     get_concept_desc(epoc.registration_status_concept_id) reg_status_desc,&lt;br /&gt;
     get_concept_code(epoc.registration_type_concept_id) registration_type,&lt;br /&gt;
     get_concept_desc(epoc.registration_type_concept_id) registration_type_desc,&lt;br /&gt;
     DATE_FORMAT(epoc.date_registered,&amp;quot;%Y%m%d&amp;quot;) date_registered,&lt;br /&gt;
     DATE_FORMAT(epoc.date_registered_end,&amp;quot;%Y%m%d&amp;quot;) date_registered_end,&lt;br /&gt;
     epoc.id sequence_number,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(6,epoc.id),0),0)  last_modified_date&lt;br /&gt;
FROM episode_of_care epoc JOIN patient pat ON epoc.patient_id = pat.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Patient Address Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     padr.id address_id,&lt;br /&gt;
     padr.address_line_1 address1,&lt;br /&gt;
     padr.address_line_2 address2,&lt;br /&gt;
     padr.address_line_3 address3,&lt;br /&gt;
     padr.address_line_4 address4,&lt;br /&gt;
     padr.city address5,&lt;br /&gt;
     padr.postcode postcode,&lt;br /&gt;
     parn.uprn,&lt;br /&gt;
     DATE_FORMAT(padr.start_date,&amp;quot;%Y%m%d&amp;quot;) start_date,&lt;br /&gt;
     DATE_FORMAT(padr.end_date,&amp;quot;%Y%m%d&amp;quot;) end_date,&lt;br /&gt;
     padr.lsoa_2001_code,&lt;br /&gt;
     padr.lsoa_2011_code,&lt;br /&gt;
     padr.msoa_2001_code,&lt;br /&gt;
     padr.msoa_2011_code,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     parn.abp_address_number matched_address_1,&lt;br /&gt;
     parn.abp_address_street matched_address_2,&lt;br /&gt;
     parn.abp_address_locality matched_address_3,&lt;br /&gt;
     parn.abp_address_town matched_address_4,&lt;br /&gt;
     parn.abp_address_postcode matched_postcode,&lt;br /&gt;
     parn.classification,&lt;br /&gt;
     parn.abp_address_organization business_name,&lt;br /&gt;
     parn.match_date uprn_match_date,&lt;br /&gt;
     parn.status uprn_status,&lt;br /&gt;
     parn.latitude,&lt;br /&gt;
     parn.longitude,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(20,padr.id),0),0)  last_modified_date&lt;br /&gt;
FROM patient_address padr JOIN patient pat ON padr.patient_id = pat.id&lt;br /&gt;
LEFT JOIN patient_address_match parn ON padr.id = parn.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Appointment Slot Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     appt.id slot_id,&lt;br /&gt;
     get_ods(appt.organization_id) source_origanization,&lt;br /&gt;
     appt.schedule_id session_id,&lt;br /&gt;
     DATE_FORMAT(appt.start_date,&amp;quot;%Y%m%d %T&amp;quot;) start_time,&lt;br /&gt;
     appt.planned_duration planned_duration,&lt;br /&gt;
     appt.actual_duration actual_duration,&lt;br /&gt;
     get_concept_desc(appt.appointment_status_concept_id) status,&lt;br /&gt;
     DATE_FORMAT(appt.date_time_sent_in,&amp;quot;%Y%m%d %T&amp;quot;) actual_start_time,&lt;br /&gt;
     appt.patient_wait wait_time,&lt;br /&gt;
     DATE_FORMAT(appt.cancelled_date,&amp;quot;%Y%m%d %T&amp;quot;) date_cancelled,&lt;br /&gt;
     DATE_FORMAT(appt.date_time_left,&amp;quot;%Y%m%d %T&amp;quot;) time_left,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(18,appt.id),0),0)  last_modified_date&lt;br /&gt;
FROM appointment appt JOIN patient pat ON appt.patient_id = pat.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Encounter Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     enc.id encounter_id,&lt;br /&gt;
     get_ods(enc.organization_id) source_organization,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     DATE_FORMAT(enc.clinical_effective_date,&amp;quot;%Y%m%d %T&amp;quot;) encounter_startdate,&lt;br /&gt;
     DATE_FORMAT(enc.end_date,&amp;quot;%Y%m%d %T&amp;quot;) encounter_enddate,&lt;br /&gt;
     get_concept_desc(enc.non_core_concept_id) original_type,&lt;br /&gt;
     prac.role_desc hcp_role,&lt;br /&gt;
     enc.type encounter_type,&lt;br /&gt;
     get_ods(enc.service_provider_organization_id) event_organization,&lt;br /&gt;
     enc.appointment_id appointment_slot_id,&lt;br /&gt;
    NULLIF(GREATEST(COALESCE(get_event_maxdate(5,enc.id),0),&lt;br /&gt;
                    COALESCE(get_event_maxdate(13,prac.id),0)),0) last_modified_date&lt;br /&gt;
FROM encounter enc JOIN patient pat ON enc.patient_id = pat.id&lt;br /&gt;
LEFT JOIN practitioner prac ON prac.id = enc.practitioner_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Organization Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     org.id organization_id,&lt;br /&gt;
     org.ods_code,&lt;br /&gt;
     org.name organization_name,&lt;br /&gt;
     org.type_code,&lt;br /&gt;
     org.type_desc,&lt;br /&gt;
     org.postcode,&lt;br /&gt;
     org.parent_organization_id,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(12,org.id),0),0)  last_modified_date&lt;br /&gt;
FROM organization org;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Appointment Session Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     sch.id session_id,&lt;br /&gt;
     sch.name session_name,&lt;br /&gt;
     get_ods(sch.organization_id) source_organization,&lt;br /&gt;
     prac.role_desc hpc_role,&lt;br /&gt;
     DATE_FORMAT(sch.start_date,&amp;quot;%Y%m%d %T:%f&amp;quot;) start_time,&lt;br /&gt;
     sch.type session_type,&lt;br /&gt;
     sch.location location,&lt;br /&gt;
     NULLIF(GREATEST(COALESCE(get_event_maxdate(17,sch.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate(13,prac.id),0)),0) last_modified_date&lt;br /&gt;
FROM schedule sch JOIN practitioner prac ON prac.id = sch.practitioner_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>AndreHui</name></author>
	</entry>
	<entry>
		<id>https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_2)&amp;diff=1576</id>
		<title>Remote Subscriber Database (RSD) SQL guide (Compass 2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_2)&amp;diff=1576"/>
		<updated>2020-06-18T09:34:09Z</updated>

		<summary type="html">&lt;p&gt;AndreHui: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This article describes the steps needed to create, and analyse, patient data selections from the Discovery database.&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This outlines the steps we take to create the patient selection for analysis from the Discovery database.&lt;br /&gt;
&lt;br /&gt;
To add this data to a report, we create datasets. Each dataset represents the result set from running a combination of SQL commands and stored procedures on a data source.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 1.1 shows the algorithm we follow to generate these datasets&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD SQL Guide C2 Fig 1.1.jpg|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%201.1.jpg|frameless|633x633px]]&lt;br /&gt;
&lt;br /&gt;
= Creating the Patient Cohort =&lt;br /&gt;
This is the first step when we receive a list of patients marked for observational study.&lt;br /&gt;
&lt;br /&gt;
===== General Patient Cohort =====&lt;br /&gt;
&#039;&#039;&#039;Fig 2.1 Entity diagram showing the relationships between patient and organization&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD SQL Guide C2 Fig 2.1.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%202.1.png|frameless|494x494px]]&lt;br /&gt;
&lt;br /&gt;
We filter patients by their organization ODS codes to ensure only patients of the required GP practices are selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example 1.1 Building patient cohort SQL with NHS numbers&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS cohort_patient;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_patient AS&lt;br /&gt;
SELECT&lt;br /&gt;
       p.id AS patient_id,&lt;br /&gt;
       p.nhs_number,&lt;br /&gt;
       p.person_id AS group_by,&lt;br /&gt;
       p.organization_id,&lt;br /&gt;
       p.date_of_birth -- add additional columns if required   &lt;br /&gt;
FROM patient p JOIN organization org ON p.organization_id = org.id&lt;br /&gt;
WHERE org.ods_code IN (&#039;EMIS99&#039;,&#039;5PC64&#039;);  &lt;br /&gt;
&lt;br /&gt;
-- add ods codes inside the brackets e.g. EMIS99, 5PC64&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Cohort with Filtered Observations =====&lt;br /&gt;
Sometimes, the cohort is created with filtered observations.&lt;br /&gt;
&lt;br /&gt;
In the example below, we create a cohort where the patients are registered, alive and have diabetes. They belong to age 12 and over.&lt;br /&gt;
&lt;br /&gt;
Here we have introduced additional tables for the query: observation, concept and the episode of care.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 2.2 Entity diagram showing the relationships between patient, observation and episode of care&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD SQL Guide C2 Fig 2.2.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%202.2.png|frameless|691x691px]]&lt;br /&gt;
&lt;br /&gt;
By joining the observation table we can obtain the observational information about the patient. If specific information is required, like in this case we are looking for patients with diabetes, we would need to filter the observation based on that specific clinical term. That could be in the form of a stand alone code or a list of codes: SNOMED, READ, CTV3, etc.&lt;br /&gt;
&lt;br /&gt;
The observation table does not store these codes directly but store them as concept ids. On querying the data then, the concept table will be queried first to get the list of concept ids that you are interested in for this observation.&lt;br /&gt;
&lt;br /&gt;
If you intend to filter observations by SNOMEDS then add in the concept map table which will map any legacy concept ids, that is, READ, CTV3 etc, to the SNOMED concept ids, then you can filter by SNOMED codes as shown: &amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
FROM patient p&lt;br /&gt;
JOIN observation o on o.patient_id = p.id&lt;br /&gt;
JOIN concept_map cm on cm.legacy = o.non_core_concept_id&lt;br /&gt;
-- use the non core concept id for link to the concept table&lt;br /&gt;
JOIN concept c on c.dbid = cm.core&lt;br /&gt;
WHERE c.code IN (46635009, 44054006) -- e.g. diabetes snomed codes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From the episode of care, we can obtain whether the patient is currently registered and has the appropriate registration type for this study:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
FROM patient p&lt;br /&gt;
JOIN episode_of_care e ON e.patient_id = p.id&lt;br /&gt;
JOIN concept c ON c.dbid = e.registration_type_concept_id&lt;br /&gt;
WHERE c.code = &#039;R&#039;  -- registered&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Some patients prefer to withhold consent and therefore are excluded from the cohort. We can check this in the observation table. Again we need to get a list of concept ids that you are interested in for this observation.&lt;br /&gt;
&lt;br /&gt;
For example, you could approach it in this way:&lt;br /&gt;
&lt;br /&gt;
1.        Create a lookup table to hold the consent codes:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Consent_Codes.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Consent%20Codes.png|alt=|frameless|448x448px]]&lt;br /&gt;
&lt;br /&gt;
2.        Create a view to filter out the concept ids on the observation table with these codes:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
-- filter out the observation table with the relevant consent codes&lt;br /&gt;
&lt;br /&gt;
DROP VIEW IF EXISTS obs_optout_view;&lt;br /&gt;
&lt;br /&gt;
CREATE VIEW obs_optout_view AS&lt;br /&gt;
SELECT&lt;br /&gt;
       ob.id,&lt;br /&gt;
       ob.patient_id,&lt;br /&gt;
       ob.organization_id,&lt;br /&gt;
       ob.non_core_concept_id,&lt;br /&gt;
       ob.clinical_effective_date,&lt;br /&gt;
       cpt.dbid,&lt;br /&gt;
       cpt.code,&lt;br /&gt;
       cpt.name,&lt;br /&gt;
       csc.status&lt;br /&gt;
FROM observation ob&lt;br /&gt;
JOIN concept cpt ON cpt.dbid = ob.non_core_concept_id&lt;br /&gt;
JOIN consent_code csc ON cpt.id = csc.code&lt;br /&gt;
AND cpt.id = BINARY csc.code;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3.        Using the view to create a helper function to get the latest opt-out status of the patient:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
-- function to retrieve the patient&#039;s latest opt out status&lt;br /&gt;
&lt;br /&gt;
DROP FUNCTION IF EXISTS get_optout_status;&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE FUNCTION get_optout_status(p_patient_id BIGINT,&lt;br /&gt;
                                  p_organization_id BIGINT)&lt;br /&gt;
RETURNS VARCHAR(1)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_status          VARCHAR(1);&lt;br /&gt;
DECLARE l_patient_id      BIGINT;&lt;br /&gt;
DECLARE l_organization_id BIGINT;&lt;br /&gt;
&lt;br /&gt;
  SELECT&lt;br /&gt;
         ob.patient_id,&lt;br /&gt;
         ob.organization_id,&lt;br /&gt;
         IF (ob.status = &#039;Opt-Out&#039;, 1, IF(ob.status = &#039;Opt-In&#039;,null,null))&lt;br /&gt;
         INTO l_patient_id, l_organization_id, l_status&lt;br /&gt;
  FROM obs_optout_view ob&lt;br /&gt;
  LEFT JOIN obs_optout_view ob2 ON ob2.patient_id = ob.patient_id&lt;br /&gt;
  AND (ob.clinical_effective_date &amp;lt; ob2.clinical_effective_date&lt;br /&gt;
  OR (ob.clinical_effective_date = ob2.clinical_effective_date&lt;br /&gt;
       AND ob.id &amp;lt; ob2.id))&lt;br /&gt;
  WHERE ob2.patient_id IS NULL&lt;br /&gt;
  AND ob.patient_id = p_patient_id&lt;br /&gt;
  AND ob.organization_id = p_organization_id LIMIT 1;&lt;br /&gt;
&lt;br /&gt;
RETURN l_status;&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Where 1 = opt out, null = opt in or not recorded derived from the latest observation from the consent codes.&lt;br /&gt;
&lt;br /&gt;
Putting all of the above together, patient cohort query becomes this:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_patient AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
         p.id         AS patient_id,&lt;br /&gt;
         p.person_id  AS group_by,&lt;br /&gt;
         get_optout_status (p.id, p.registered_practice_organization_id) AS optout_status,&lt;br /&gt;
         IF (p.date_of_death IS NULL,&lt;br /&gt;
              TIMESTAMPDIFF(YEAR, p.date_of_birth, CURDATE()),&lt;br /&gt;
              TIMESTAMPDIFF(YEAR, p.date_of_birth, p.date_of_death)) age_years&lt;br /&gt;
  FROM patient p&lt;br /&gt;
  JOIN observation o ON p.id = o.patient_id&lt;br /&gt;
  JOIN organization org ON org.id = o.organization_id&lt;br /&gt;
  JOIN episode_of_care e ON e.patient_id = o.patient_id&lt;br /&gt;
  JOIN concept c ON c.dbid = e.registration_type_concept_id&lt;br /&gt;
  JOIN (SELECT o.patient_id&lt;br /&gt;
        FROM observation o&lt;br /&gt;
        JOIN concept_map cm ON cm.legacy = o.non_core_concept_id&lt;br /&gt;
        JOIN concept c ON c.dbid = cm.core&lt;br /&gt;
        WHERE c.code IN (46635009, 44054006) -- e.g. snomed codes&lt;br /&gt;
        ) ob ON ob.patient_id = p.id&lt;br /&gt;
  WHERE org.ods_code IN (&#039;EMIS99&#039;,&#039;5PC64&#039;) -- e.g. ods codes&lt;br /&gt;
  AND p.date_of_death IS NULL&lt;br /&gt;
  AND c.code = &#039;R&#039;  -- registered patient&lt;br /&gt;
  AND e.date_registered &amp;lt;= now()&lt;br /&gt;
  AND (e.date_registered_end &amp;gt; now() OR&lt;br /&gt;
       e.date_registered_end IS NULL) -- active&lt;br /&gt;
  AND IF (p.date_of_death IS NULL,&lt;br /&gt;
          TIMESTAMPDIFF(YEAR, p.date_of_birth, CURDATE()),&lt;br /&gt;
          TIMESTAMPDIFF(YEAR, p.date_of_birth, p.date_of_death)) &amp;gt;= 12 -- age 12 and over&lt;br /&gt;
  AND get_optout_status(p.id, p.registered_practice_organization_id) IS NULL; -- opt in status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Creating the Observations Cohort =&lt;br /&gt;
We create a separate observations cohort for query performance. Instead of scanning through the entire observation table, we create a sub-set of the observation table based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example 2.1 Building the Observations Cohort SQL&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_observations AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
         o.id,&lt;br /&gt;
         o.person_id AS group_by,&lt;br /&gt;
         o.patient_id,&lt;br /&gt;
         o.person_id,&lt;br /&gt;
         o.clinical_effective_date,&lt;br /&gt;
         o.non_core_concept_id,&lt;br /&gt;
         o.result_value,&lt;br /&gt;
         o.result_value_units,&lt;br /&gt;
         o.result_date,&lt;br /&gt;
         o.result_text,&lt;br /&gt;
         cr.age_year&lt;br /&gt;
  FROM observation o JOIN cohort_patient cr&lt;br /&gt;
  ON o.person_id = cr.person_id&lt;br /&gt;
  AND o.organization_id = cr.organization_id&lt;br /&gt;
  AND o.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX obs_cpt_ix ON cohort_observation(non_core_concept_id);&lt;br /&gt;
CREATE INDEX obs_pat_ix ON cohort_observation(patient_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Creating the Medications Cohort =&lt;br /&gt;
Again, we create the medications cohort for query performance. We create a sub-set of the medication statement/order table based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 4.1 Entity diagram showing the relationships between patient, medication statement and organization&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Fig_4.1.png|link=https://wiki.discoverydataservice.org/File:Fig%204.1.png|alt=|frameless|633x633px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example 4.1 Building the Medications Cohort SQL&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
-- step 1: cohort of the medication statement&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS cohort_medications_stmt;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_medications_stmt AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            m.id,&lt;br /&gt;
            m.non_core_concept_id,&lt;br /&gt;
            m.person_id,&lt;br /&gt;
            m.patient_id,&lt;br /&gt;
            m.clinical_effective_date,&lt;br /&gt;
            m.cancellation_date&lt;br /&gt;
     FROM medication_statement m&lt;br /&gt;
     JOIN cohort_patient cr&lt;br /&gt;
     ON m.organization_id = cr.organization_id&lt;br /&gt;
     AND m.person_id = cr.person_id&lt;br /&gt;
     AND m.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_stmt_ix ON cohort_medications_stmt(id);&lt;br /&gt;
&lt;br /&gt;
-- step 2: cohort of the medication oder&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS cohort_medications_ord;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_medications_ord AS  &lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            mo.id,&lt;br /&gt;
            mo.medication_statement_id&lt;br /&gt;
            mo.person_id,&lt;br /&gt;
            mo.patient_id,&lt;br /&gt;
            mo.clinical_effective_date&lt;br /&gt;
     FROM medication_order mo&lt;br /&gt;
     JOIN cohort_patient cr&lt;br /&gt;
     ON mo.organization_id = cr.organization_id&lt;br /&gt;
     AND mo.person_id = cr.person_id&lt;br /&gt;
     AND mo.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_ord_ix ON cohort_medications_ord(medication_statement_id);    &lt;br /&gt;
&lt;br /&gt;
-- step 3: cohort of the medication - statement plus order&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS cohort_medications;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE cohort_medications AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            ms.id,&lt;br /&gt;
            ms.non_core_concept_id,&lt;br /&gt;
            ms.person_id,&lt;br /&gt;
            ms.patient_id,&lt;br /&gt;
            mo.clinical_effective_date,&lt;br /&gt;
            ms.cancellation_date&lt;br /&gt;
      FROM cohort_medications_stmt ms JOIN cohort_medications_ord mo&lt;br /&gt;
      ON ms.id = mo.medication_statement_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_ix ON cohort_medications(non_core_concept_id);&lt;br /&gt;
CREATE INDEX med_pat_ix ON cohort_medications(patient_id);        &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Building Datasets =&lt;br /&gt;
This involves creating a number of temporary tables in the database.&lt;br /&gt;
&lt;br /&gt;
===== Patient Demographic Dataset =====&lt;br /&gt;
A typical patient demographic dataset may consist of the following information:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Example_Patient_Demographic_Dataset.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Example%20Patient%20Demographic%20Dataset.png|alt=|frameless|1294x1294px]]&lt;br /&gt;
&lt;br /&gt;
For this example, we create the dataset to hold the patient demographic information as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE demographicsDataset (&lt;br /&gt;
  ExtractDate                    DATETIME     NULL,&lt;br /&gt;
  Pseudo_id                      VARCHAR(255) NULL,   -- unique identifier&lt;br /&gt;
  Pseudo_NHSNumber               VARCHAR(255) NULL,&lt;br /&gt;
  Gender                         VARCHAR(50)  NULL,&lt;br /&gt;
  Age                            VARCHAR(50)  NULL,&lt;br /&gt;
  DateOfBirth                    DATE         NULL,&lt;br /&gt;
  EthnicityLCode                 VARCHAR(50)  NULL,&lt;br /&gt;
  EthnicityLTerm                 VARCHAR(200) NULL,&lt;br /&gt;
  BirthCountryLCode              VARCHAR(50)  NULL,&lt;br /&gt;
  BirthCountryLTerm              VARCHAR(200) NULL,&lt;br /&gt;
  RegistrationStart              DATE         NULL,&lt;br /&gt;
  RegistrationEnd                DATE         NULL,&lt;br /&gt;
  IMD2010                        VARCHAR(50)  NULL,&lt;br /&gt;
  LSOA2011                       VARCHAR(50)  NULL,&lt;br /&gt;
  PracticeODSCode                VARCHAR(50)  NULL,&lt;br /&gt;
  PracticeODSName                VARCHAR(255) NULL,&lt;br /&gt;
  CCGName                        VARCHAR(100) NULL,&lt;br /&gt;
  YearOfDeath                    INT(4)       NULL,&lt;br /&gt;
  F2fVisits_Total                INT(11)      DEFAULT 0,&lt;br /&gt;
  F2fVisits_1year                INT(11)      DEFAULT 0,&lt;br /&gt;
  F2fVisits_5years               INT(11)      DEFAULT 0&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE demographicsDataset ADD INDEX demo_pseudoid_idx (pseudo_id);&lt;br /&gt;
&lt;br /&gt;
-- the unique identifier is usually the pseudo id&lt;br /&gt;
-- but to adapt for this schema we could use the person_id&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We pre-populate the dataset with the unique identifiers from the patient cohort:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
INSERT INTO demographicsDataset (pseudo_id, extractdate) SELECT DISTINCT group_by, now() FROM cohort_patient;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we run separate update SQL queries to populate the columns of the dataset table.&lt;br /&gt;
&lt;br /&gt;
For example, add registration start date, registration end date, practice ods code, practice ods name:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS reg_sort;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_reg;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_reg AS&lt;br /&gt;
SELECT c.group_by,&lt;br /&gt;
       e.date_regirstered,&lt;br /&gt;
       e.date_registered_end,&lt;br /&gt;
       o.name,&lt;br /&gt;
       o.ods_code&lt;br /&gt;
FROM cohort_patient c&lt;br /&gt;
JOIN episode_of_care e ON e.person_id = c.group_by&lt;br /&gt;
JOIN organization o ON o.id = e.organization_id&lt;br /&gt;
WHERE o.ods_code IN (&#039;EMIS99&#039;,&#039;5PC64&#039;); -- e.g. ods codes&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE reg_sort (&lt;br /&gt;
      row_id               INT,&lt;br /&gt;
      group_by             BIGINT,&lt;br /&gt;
      date_registered      DATE,&lt;br /&gt;
      date_regiostered_end DATE,&lt;br /&gt;
      ods_code             VARCHAR(50),&lt;br /&gt;
      name                 VARCHAR(255),&lt;br /&gt;
      rnk                  INT, PRIMARY KEY(row_id)) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
        a.group_by,&lt;br /&gt;
        a.date_registered,&lt;br /&gt;
        a.date_registered_end,&lt;br /&gt;
        a.ods_code,      &lt;br /&gt;
        a.name,&lt;br /&gt;
        a.rnk&lt;br /&gt;
FROM (SELECT q.group_by,&lt;br /&gt;
             q.date_registered,&lt;br /&gt;
             q.date_registered_end,&lt;br /&gt;
             q.name,&lt;br /&gt;
             q.ods_code,&lt;br /&gt;
             @currank := IF(@curperson = q.group_by, @currank + 1, 1) AS rnk,&lt;br /&gt;
             @curperson := q.group_by AS cur_person&lt;br /&gt;
      FROM qry_reg q, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
      ORDER BY q.group_by, q.date_registered DESC ) a,&lt;br /&gt;
      (SELECT @row_no := 0) t&lt;br /&gt;
WHERE a.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from reg_sort&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
       UPDATE demographicsDataset d&lt;br /&gt;
       JOIN reg_sort reg ON d.pseudo_id = reg.group_by&lt;br /&gt;
       SET d.RegistrationStart = reg.date_registered,&lt;br /&gt;
           d.RegistrationEnd = reg.date_registered_end,&lt;br /&gt;
           d.PracticeODSCode = reg.ods_code,&lt;br /&gt;
           d.PracticeODSName = reg.name&lt;br /&gt;
       WHERE reg.row_id &amp;gt; @row_id&lt;br /&gt;
       AND reg.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
       SET @row_id = @row_id + 1000;  &lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In this example, we created 2 temporary tables. The first to hold the registration data. The second to select from the first the records which contain the latest registration date.&lt;br /&gt;
&lt;br /&gt;
If the update is large in terms of the number of records to update then it might be possible to optimise the update process by using batches as demonstrated in the code example above.&lt;br /&gt;
&lt;br /&gt;
===== Patient Observations Dataset =====&lt;br /&gt;
A typical patient observations dataset may ask for the following information:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Example_Patient_Observations_dataset.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Example%20Patient%20Observations%20dataset.png|alt=|frameless|1122x1122px]]&lt;br /&gt;
&lt;br /&gt;
A typical dataset can have over 100 to 200 fields. Usually, there will be several datasets to deliver. But this illustrates the kind of layout we use for constructing an observational dataset.&lt;br /&gt;
&lt;br /&gt;
For this example, we create the dataset to hold the observational information as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS diagnoses2dataset;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE diagnoses2dataset (&lt;br /&gt;
  Pseudo_id                                        VARCHAR(255) NULL,&lt;br /&gt;
  Pseudo_NHSNumber                                 VARCHAR(255) NULL,&lt;br /&gt;
  AsthmaECode                                      VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaETerm                                      VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaEDate                                      VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeECode                                VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeETerm                                VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaEmergeEDate                                VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedECode                              VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedETerm                              VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaResolvedEDate                              VARCHAR(50) NULL,&lt;br /&gt;
  COPDECode                                        VARCHAR(50) NULL,&lt;br /&gt;
  COPDETerm                                        VARCHAR(200) NULL,&lt;br /&gt;
  COPDEDate                                        VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisECode                           VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisETerm                           VARCHAR(200) NULL,&lt;br /&gt;
  PulmonaryFibrosisEDate                           VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseECode                     VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseETerm                     VARCHAR(200) NULL,&lt;br /&gt;
  InterstitialLungDiseaseEDate                     VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationECode              VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationETerm              VARCHAR(200) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationEDate              VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaECode                                    VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaETerm                                    VARCHAR(200) NULL,&lt;br /&gt;
  GlaucomaEDate                                    VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisECode                         VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisETerm                         VARCHAR(200) NULL,&lt;br /&gt;
  RheumatoidArthritisEDate                         VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusECode                               VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusETerm                               VARCHAR(200) NULL,&lt;br /&gt;
  SystemicLupusEDate                               VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseECode                    VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseETerm                    VARCHAR(200) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseEDate                    VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseECode                               VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseETerm                               VARCHAR(200) NULL,&lt;br /&gt;
  CrohnsDiseaseEDate                               VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeECode                       VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeETerm                       VARCHAR(200) NULL,&lt;br /&gt;
  UlcerativeColitisCodeEDate                       VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisECode                            VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisETerm                            VARCHAR(200) NULL,&lt;br /&gt;
  AtopicDermatitisEDate                            VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceECode               VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceETerm               VARCHAR(200) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceEDate               VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaECode                    VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaETerm                    VARCHAR(200) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaEDate                    VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaECode                                    VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaETerm                                    VARCHAR(200) NULL,&lt;br /&gt;
  MelanomaEDate                                    VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerECode                              VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerETerm                              VARCHAR(200) NULL,&lt;br /&gt;
  ProstateCancerEDate                              VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerECode                                  VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerETerm                                  VARCHAR(200) NULL,&lt;br /&gt;
  LungCancerEDate                                  VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerECode                            VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerETerm                            VARCHAR(200) NULL,&lt;br /&gt;
  SmallBowelCancerEDate                            VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerECode                            VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerETerm                            VARCHAR(200) NULL,&lt;br /&gt;
  ColorectalCancerEDate                            VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerECode                                VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerETerm                                VARCHAR(200) NULL,&lt;br /&gt;
  BreastCancerEDate                                VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageECode                                 VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageETerm                                 VARCHAR(200) NULL,&lt;br /&gt;
  MiscarriageEDate                                 VARCHAR(50) NULL&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE diagnoses2dataset ADD INDEX pseudoid_idx (pseudo_id);&lt;br /&gt;
&lt;br /&gt;
INSERT INTO diagnoses2dataset (pseudo_id)&lt;br /&gt;
SELECT DISTINCT group_by FROM cohort_patient;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Each column of the dataset is named by joining pieces of identifiable information together by combining the observational type, the timeframe for the analysis and the requested field type.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 5.1 Shows the naming convention of a dataset column&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_5.1.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%205.1.png|alt=|frameless|744x744px]]&lt;br /&gt;
&lt;br /&gt;
Again, we pre-populate the dataset with the unique identifiers from the patient cohort.&lt;br /&gt;
&lt;br /&gt;
Then we run separate update SQL queries to populate the dataset table by calling multiple stored procedures.&lt;br /&gt;
&lt;br /&gt;
===== Patient Medications Dataset =====&lt;br /&gt;
A typical medication request would look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Medication_Request.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Medication%20Request.png|alt=|frameless|1235x1235px]]&lt;br /&gt;
&lt;br /&gt;
For this example, we create the dataset to hold the medication information as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE medications (&lt;br /&gt;
  pseudo_id            VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  nhsnumber            VARCHAR(10)  DEFAULT NULL,&lt;br /&gt;
  codedate             VARCHAR(20)  DEFAULT NULL,&lt;br /&gt;
  codeterm             VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  code                 VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codevalue            VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codeunit             VARCHAR(100) DEFAULT NULL&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Again, we pre-populate the dataset with the unique identifiers from the patient cohort.&lt;br /&gt;
&lt;br /&gt;
Then we run separate update SQL queries to populate the dataset table by calling multiple stored procedures.&lt;br /&gt;
&lt;br /&gt;
= Calling Stored Procedures =&lt;br /&gt;
Once we have generated the observation and medication datasets and cohorts, the next step is to populate the datasets using stored procedures.&lt;br /&gt;
&lt;br /&gt;
We construct a series of populate stored procedures which in turn call on other stored procedures to complete the update process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 6.1 Shows the datasets update process using stored procedures&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_6.1.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%206.1.png|alt=|frameless|590x590px]]&amp;lt;br /&amp;gt;&lt;br /&gt;
=== Calling the Populate Stored Procedures ===&lt;br /&gt;
Create a SQL script to call the stored procedure as follows:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;StillbirthE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;237364002,161743003&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;PregnancyInducedHypertensionE&#039;,&#039;diagnoses2dataset&#039;,0,null,&#039;48194001&#039;,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;PreEclampsiaE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;398254007&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;CholestasisE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;235888006&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;GallstonesE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;266474003,407637009&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;GoutE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;90560007,161451004&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;AnkylosingSpondylitisE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;9631008&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;JaundiceE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;18165001,161536006&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;PsoriasisE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;9014002&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;DeafnessE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;15188001&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate&lt;br /&gt;
(0,&#039;HearingAidE&#039;,&#039;diagnoses2dataset&#039;,0,&#039;365240006&#039;,null,null,null,&#039;N&#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The procedure accepts a list of input parameters (this may vary depending on requirements):&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Parameter&lt;br /&gt;
|Data Type&lt;br /&gt;
|Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
|-&lt;br /&gt;
|col&lt;br /&gt;
|VARCHAR(100)&lt;br /&gt;
|the root of the column name&lt;br /&gt;
|-&lt;br /&gt;
|datasetTable&lt;br /&gt;
|VARCHAR(100)&lt;br /&gt;
|table name of dataset&lt;br /&gt;
|-&lt;br /&gt;
|reset&lt;br /&gt;
|BIT&lt;br /&gt;
|1 reset, 0 no reset of the dataset table&lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd1&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|include all snomed parents and their  children&lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd2&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|include snomed parents only&lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove3&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|exclude snomed parents and their children&lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove4&lt;br /&gt;
|VARCHAR(5000)&lt;br /&gt;
|exclude only snomed parents&lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls&lt;br /&gt;
|VARCHAR(1)&lt;br /&gt;
|Y or N - whether to ignore null result  value&lt;br /&gt;
|}&lt;br /&gt;
A typical populate procedure is outlined below. (Please note this is only an example and may need modifications to it depending on your requirements).&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP PROCEDURE IF EXISTS populateCodeDate;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE populateCodeDate (&lt;br /&gt;
IN filterType     INT, -- 1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
IN col            VARCHAR(100), -- the root of the column name&lt;br /&gt;
IN datasetTable   VARCHAR(100), -- table name of dataset&lt;br /&gt;
IN reset          BIT, -- 1 reset, 0 no reset&lt;br /&gt;
IN codesToAdd1    VARCHAR(5000), -- all parents and their children&lt;br /&gt;
IN codesToAdd2    VARCHAR(5000), -- parents only&lt;br /&gt;
IN codesToRemove3 VARCHAR(5000), -- parents and their children to be excluded&lt;br /&gt;
IN codesToRemove4 VARCHAR(5000), -- just parents to be excluded&lt;br /&gt;
IN ignorenulls    VARCHAR(1)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
-- reset snomeds table&lt;br /&gt;
DELETE FROM snomeds WHERE cat_id IN (1, 2, 3, 4);&lt;br /&gt;
-- reset store table&lt;br /&gt;
DELETE FROM store WHERE id IN (1, 2, 3, 4);&lt;br /&gt;
&lt;br /&gt;
-- get snomed concept ids&lt;br /&gt;
&lt;br /&gt;
  IF codesToAdd1 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString(codesToAdd1, 1);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (1);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
  IF codesToAdd2 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString (codesToAdd2, 2);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (2);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
  IF codesToRemove3 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString (codesToRemove3, 3);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (3);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
  IF codesToRemove4 IS NOT NULL THEN&lt;br /&gt;
      CALL storeSnomedString (codesToRemove4, 4);&lt;br /&gt;
      CALL getAllSnomedsConceptIds (4);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
-- filter observation based on the snomed codes&lt;br /&gt;
&lt;br /&gt;
CALL filterObservations(filterType, 1, ignorenulls);&lt;br /&gt;
&lt;br /&gt;
-- reset columns&lt;br /&gt;
&lt;br /&gt;
IF (reset = 1) THEN&lt;br /&gt;
&lt;br /&gt;
  SET @reset_sql = CONCAT(&#039;UPDATE &#039;, datasetTable, &#039; SET &#039;,&lt;br /&gt;
  col, &amp;quot;Code = null, &amp;quot;,&lt;br /&gt;
  col, &amp;quot;Term = null, &amp;quot;,&lt;br /&gt;
  col, &amp;quot;Date = null&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  PREPARE resetStmt FROM @reset_sql;&lt;br /&gt;
  EXECUTE resetStmt;&lt;br /&gt;
  DEALLOCATE PREPARE resetStmt;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
  DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
  CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
      row_id                  INT,&lt;br /&gt;
      group_by                VARCHAR(255),&lt;br /&gt;
      original_code           VARCHAR(20),&lt;br /&gt;
      original_term           VARCHAR(200),&lt;br /&gt;
      clinical_effective_date DATE, PRIMARY KEY(row_id)&lt;br /&gt;
  ) AS&lt;br /&gt;
&lt;br /&gt;
  SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
         f.group_by,&lt;br /&gt;
         f.original_code,&lt;br /&gt;
         f.original_term,&lt;br /&gt;
         f.clinical_effective_date&lt;br /&gt;
  FROM filteredObservations f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
             &#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
             col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
             col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
             col, &amp;quot;Date = date_format(f.clinical_effective_date, &#039;%d/%m/%Y&#039;)&lt;br /&gt;
             WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
  PREPARE stmt FROM @sql;&lt;br /&gt;
  EXECUTE stmt;&lt;br /&gt;
  DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Retrieving the SNOMED Concept Ids&#039;&#039;&#039; ====&lt;br /&gt;
The procedure accepts a list of SNOMED codes as input parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 6.2 Shows how the SNOMED codes are being retrieved using stored procedures&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_6.2.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%206.2.png|alt=|frameless|818x818px]]&amp;lt;br /&amp;gt;&lt;br /&gt;
===== Calling the storeSnomedString procedure =====&lt;br /&gt;
We call the storeSnomedString procedure to separate the list of SNOMED codes into their individual values and store them into a temporary table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS store;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE store (&lt;br /&gt;
  id            INT,&lt;br /&gt;
  org_snomed_id BIGINT&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE store ADD INDEX store_idx (org_snomed_id);&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS storeSnomedString;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE PROCEDURE storeSnomedString (&lt;br /&gt;
  IN stringValue VARCHAR(5000),&lt;br /&gt;
  IN cat_id      INT&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
  DECLARE front       VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
  DECLARE frontlen    INT           DEFAULT NULL;&lt;br /&gt;
  DECLARE TempValue   VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
&lt;br /&gt;
  processloop:&lt;br /&gt;
  LOOP&lt;br /&gt;
&lt;br /&gt;
      IF LENGTH(TRIM(stringValue)) = 0&lt;br /&gt;
      OR stringValue IS NULL THEN&lt;br /&gt;
        LEAVE process loop;&lt;br /&gt;
      END IF;&lt;br /&gt;
   &lt;br /&gt;
  SET front = SUBSTRING_INDEX(stringValue, &#039;,&#039;, 1);&lt;br /&gt;
  SET frontlen = LENGTH(front);&lt;br /&gt;
  SET TempValue = TRIM(front);&lt;br /&gt;
&lt;br /&gt;
  INSERT INTO store (id, org_snomed_id)&lt;br /&gt;
  VALUES (cat_id, CAST(TempValue AS SIGNED));&lt;br /&gt;
&lt;br /&gt;
  SET stringValue = INSERT(stringValue, 1, frontlen + 1, &#039;&#039;);&lt;br /&gt;
&lt;br /&gt;
  END LOOP;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Calling the getAllSnomedsConceptIds procedure =====&lt;br /&gt;
We call another procedure getAllSnomedsConceptIdsFromSnomedCodes to loop through the temporary table. For each SNOMED code, we retrieve the corresponding SNOMED concept id.&lt;br /&gt;
&lt;br /&gt;
We store the resultant set of concept ids into another temporary table where it will be used to query observational data.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS snomeds;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE snomeds (&lt;br /&gt;
  cat_id               INT,&lt;br /&gt;
  snomed_concept_id    BIGINT,&lt;br /&gt;
  core_concept         INT,&lt;br /&gt;
  snomed_code          BIGINT,&lt;br /&gt;
  term                 VARCHAR(255)&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX sno_cpt_idx(snomed_concept_id);&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX sno_code_idx(snomed_code);&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS getAllSnomedsConceptIds;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE getAllSnomedsConceptIds(p_cat_id INT)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
  DECLARE done           INT;&lt;br /&gt;
  DECLARE l_parent_id    BIGINT;&lt;br /&gt;
  DECLARE c_get_snomeds  CURSOR FOR SELECT org_snomed_id&lt;br /&gt;
                         FROM store WHERE id = p_cat_id;&lt;br /&gt;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;&lt;br /&gt;
&lt;br /&gt;
  SET done = 0;&lt;br /&gt;
&lt;br /&gt;
       OPEN c_get_snomeds;&lt;br /&gt;
&lt;br /&gt;
       processloop:&lt;br /&gt;
       WHILE (done = 0) DO&lt;br /&gt;
&lt;br /&gt;
        FETCH c_get_snomeds INTO l_parent_id;&lt;br /&gt;
&lt;br /&gt;
        IF done = 1 THEN&lt;br /&gt;
           LEAVE processloop;&lt;br /&gt;
        END IF;&lt;br /&gt;
&lt;br /&gt;
        IF p_cat_id IN (1, 3) THEN&lt;br /&gt;
&lt;br /&gt;
          -- get parent snomed concept id based on the snomed code&lt;br /&gt;
&lt;br /&gt;
          INSERT INTO snomeds&lt;br /&gt;
          SELECT&lt;br /&gt;
                p_cat_id,&lt;br /&gt;
                cm.legacy,&lt;br /&gt;
                cm.core,&lt;br /&gt;
                c.code,&lt;br /&gt;
                c.name&lt;br /&gt;
           FROM concept_map cm JOIN concept c ON c.dbid = cm.core&lt;br /&gt;
           WHERE c.code = l_parent_id;&lt;br /&gt;
&lt;br /&gt;
           -- get all child snomed concept ids based on the parent snomed code&lt;br /&gt;
&lt;br /&gt;
           INSERT INTO snomeds&lt;br /&gt;
           SELECT&lt;br /&gt;
                 p_cat_id,&lt;br /&gt;
                 cptm.legacy,&lt;br /&gt;
                 cptm.core,&lt;br /&gt;
                 cpt.code,&lt;br /&gt;
                 cpt.name&lt;br /&gt;
            FROM concept_tct tc&lt;br /&gt;
            JOIN snomeds sn ON sn.core_concept = tc.target&lt;br /&gt;
            AND tc.property = &#039;92842&#039;&lt;br /&gt;
            JOIN concept_map cptm on cptm.core = tc.source&lt;br /&gt;
            AND tc.property = &#039;92842&#039;&lt;br /&gt;
            JOIN concept cpt on cpt.dbid = cptm.core&lt;br /&gt;
            WHERE sn.snomed_code = l_parent_id;&lt;br /&gt;
&lt;br /&gt;
        ELSE&lt;br /&gt;
&lt;br /&gt;
        -- include or exclude parent snomed&lt;br /&gt;
&lt;br /&gt;
            INSERT INTO snomeds&lt;br /&gt;
            SELECT&lt;br /&gt;
                  p_cat_id,&lt;br /&gt;
                  cm.legacy,&lt;br /&gt;
                  cm.core,&lt;br /&gt;
                  c.code,&lt;br /&gt;
                  c.name&lt;br /&gt;
             FROM concept_map cm JOIN concept c ON c.dbid = cm.core&lt;br /&gt;
             WHERE c.code = l_parent_id;&lt;br /&gt;
&lt;br /&gt;
        END IF;&lt;br /&gt;
&lt;br /&gt;
       END WHILE processloop;&lt;br /&gt;
       CLOSE c_get_snomeds;&lt;br /&gt;
&lt;br /&gt;
       SET done = 0;  &lt;br /&gt;
&lt;br /&gt;
END //&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Using the concept_tct table, which is a transitive closure table, we are able to find the corresponding child concept ids based on the parent concept id.&lt;br /&gt;
&lt;br /&gt;
Match the parent against the value in the target column. This will return all the children in the source column.&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Concepts_tct.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Concepts%20tct.png|alt=|frameless]]&lt;br /&gt;
&lt;br /&gt;
==== Retrieving filtered Observations ====&lt;br /&gt;
Using the prepared observations cohort, the next step is to filter out this cohort based on the SNOMED code list and the timeframe, which have been passed in as input parameters from the populate procedure.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fig 6.3 Shows the processing of the FilterObservations procedure&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:RSD_SQL_Guide_C2_Fig_6.3.png|link=https://wiki.discoverydataservice.org/File:RSD%20SQL%20Guide%20C2%20Fig%206.3.png|alt=|frameless|596x596px]]&amp;lt;br /&amp;gt;&lt;br /&gt;
===== Calling the filterObservations procedure =====&lt;br /&gt;
By calling the filterObservations procedure, we pass in the following input parameters:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Parameter&lt;br /&gt;
|Data Type&lt;br /&gt;
|Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|Timeframe of the analysis. 0 = earliest, 1  = latest, 2 = ever, 3 = pivot around cohort pivot date (6 months), 4 = All  since&lt;br /&gt;
|-&lt;br /&gt;
|toCreate&lt;br /&gt;
|INT&lt;br /&gt;
|1 is the default value - to call  createObservationsFromCohort&lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls&lt;br /&gt;
|VARCHAR(1)&lt;br /&gt;
|Y or N - whether to ignore null result  value&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP PROCEDURE IF EXISTS filterObservations;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE PROCEDURE filterObservations (&lt;br /&gt;
     IN filterType INT,&lt;br /&gt;
-- 0 earliest, 1 latest, 2 ever,&lt;br /&gt;
-- 3 pivot around cohort pivot date (6 months),&lt;br /&gt;
-- 4 allSince&lt;br /&gt;
  IN toCreate   INT, -- 1 to createObservationsFromCohortv2&lt;br /&gt;
  IN ignorenulls VARCHAR(1) -- Y or N&lt;br /&gt;
  )&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
IF (toCreate = 1) THEN&lt;br /&gt;
  CALL createObservationsFromCohort (filterType);&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS filteredObservations;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 0) THEN -- earliest&lt;br /&gt;
&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN  &lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT&lt;br /&gt;
          ob.id,&lt;br /&gt;
          ob.group_by,&lt;br /&gt;
          ob.patient_id,&lt;br /&gt;
          ob.person_id,&lt;br /&gt;
          ob.original_code,&lt;br /&gt;
          ob.original_term,&lt;br /&gt;
          ob.result_value,&lt;br /&gt;
          ob.clinical_effective_date,&lt;br /&gt;
          ob.result_value_units,&lt;br /&gt;
          ob.age_years,&lt;br /&gt;
          ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.original_code,&lt;br /&gt;
            o.original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years,&lt;br /&gt;
            @currank :=&lt;br /&gt;
                 IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
            @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          WHERE o.result_value IS NOT NULL&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date ASC,&lt;br /&gt;
                   o.id ASC -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
  ELSE&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT&lt;br /&gt;
          ob.id,&lt;br /&gt;
          ob.group_by,&lt;br /&gt;
          ob.patient_id,&lt;br /&gt;
          ob.person_id,&lt;br /&gt;
          ob.original_code,&lt;br /&gt;
          ob.original_term,&lt;br /&gt;
          ob.result_value,&lt;br /&gt;
          ob.clinical_effective_date,&lt;br /&gt;
          ob.result_value_units,&lt;br /&gt;
          ob.age_years,&lt;br /&gt;
          ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.original_code,&lt;br /&gt;
            o.original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years,&lt;br /&gt;
            @currank :=&lt;br /&gt;
                  IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
            @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date ASC,&lt;br /&gt;
                   o.id ASC -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
ELSEIF (filterType = 2 OR filterType = 4) THEN -- ever or allSince&lt;br /&gt;
&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
          mc.group_by,&lt;br /&gt;
          mc.patient_id,&lt;br /&gt;
          mc.person_id,&lt;br /&gt;
          mc.original_code,&lt;br /&gt;
          mc.original_term,&lt;br /&gt;
          mc.result_value,&lt;br /&gt;
          mc.clinical_effective_date,&lt;br /&gt;
          mc.result_value_units,&lt;br /&gt;
          mc.age_years&lt;br /&gt;
  FROM observationsFromCohort mc&lt;br /&gt;
  WHERE mc.result_value IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
  ELSE&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
          mc.group_by,&lt;br /&gt;
          mc.patient_id,&lt;br /&gt;
          mc.person_id,&lt;br /&gt;
          mc.original_code,&lt;br /&gt;
          mc.original_term,&lt;br /&gt;
          mc.result_value,&lt;br /&gt;
          mc.clinical_effective_date,&lt;br /&gt;
          mc.result_value_units,&lt;br /&gt;
          mc.age_years&lt;br /&gt;
  FROM observationsFromCohort mc;&lt;br /&gt;
&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ELSEIF (filterType = 1 or filterType = 3) THEN -- latest or pivot&lt;br /&gt;
&lt;br /&gt;
IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE filteredObservations as&lt;br /&gt;
  SELECT&lt;br /&gt;
           ob.id,&lt;br /&gt;
           ob.group_by,&lt;br /&gt;
           ob.patient_id,&lt;br /&gt;
           ob.person_id,&lt;br /&gt;
           ob.original_code,&lt;br /&gt;
           ob.original_term,&lt;br /&gt;
           ob.result_value,&lt;br /&gt;
           ob.clinical_effective_date,&lt;br /&gt;
           ob.result_value_units,&lt;br /&gt;
           ob.age_years,&lt;br /&gt;
           ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.original_code,&lt;br /&gt;
            o.original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years,&lt;br /&gt;
            @currank :=&lt;br /&gt;
                IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
            @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          WHERE o.result_value IS NOT NULL&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date DESC,&lt;br /&gt;
                   o.id DESC -- latest&lt;br /&gt;
        ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservationsV2 as&lt;br /&gt;
  SELECT&lt;br /&gt;
           ob.id,&lt;br /&gt;
           ob.group_by,&lt;br /&gt;
           ob.patient_id,&lt;br /&gt;
           ob.person_id,&lt;br /&gt;
           ob.original_code,&lt;br /&gt;
           ob.original_term,&lt;br /&gt;
           ob.result_value,&lt;br /&gt;
           ob.clinical_effective_date,&lt;br /&gt;
           ob.result_value_units,&lt;br /&gt;
           ob.age_years,&lt;br /&gt;
           ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
             o.id,&lt;br /&gt;
             o.group_by,&lt;br /&gt;
             o.patient_id,&lt;br /&gt;
             o.person_id,&lt;br /&gt;
             o.original_code,&lt;br /&gt;
             o.original_term,&lt;br /&gt;
             o.result_value,&lt;br /&gt;
             o.clinical_effective_date,&lt;br /&gt;
             o.result_value_units,&lt;br /&gt;
             o.age_years,&lt;br /&gt;
             @currank :=&lt;br /&gt;
                  IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
             @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date DESC,&lt;br /&gt;
                   o.id DESC -- latest&lt;br /&gt;
         ) ob&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
&lt;br /&gt;
  SIGNAL SQLSTATE &#039;45000&#039;&lt;br /&gt;
&lt;br /&gt;
  SET MESSAGE_TEXT = &#039;filterType not recognised&#039;;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Calling the createObservationsFromCohort procedure =====&lt;br /&gt;
From the filterObservations procedure, we call the createobservationsFromCohort procedure, passing in the filter type as the input parameter:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL createObservationsFromCohort (filterType);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP PROCEDURE IF EXISTS createObservationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE PROCEDURE createObservationsFromCohort (&lt;br /&gt;
  IN filterType INT&lt;br /&gt;
  -- 0 earliest, 1 latest, 2 ever,&lt;br /&gt;
  -- 3 pivot around cohort_patient pivot date (6 months)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS snomeds_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE snomeds_tmp AS&lt;br /&gt;
SELECT cat_id,&lt;br /&gt;
      snomed_concept_id&lt;br /&gt;
FROM snomeds&lt;br /&gt;
WHERE cat_id IN (3, 4);&lt;br /&gt;
&lt;br /&gt;
-- delete from snomeds all snomed_concept ids to be excluded&lt;br /&gt;
&lt;br /&gt;
DELETE t1 FROM snomeds t1 JOIN snomeds_tmp t2&lt;br /&gt;
ON t1.snomed_concept_id = t2.snomed_concept_id&lt;br /&gt;
WHERE t1.cat_id IN (1, 2);&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS observationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 3) THEN&lt;br /&gt;
&lt;br /&gt;
-- pivot over 6 months from pivot date (already set in cohort)&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE observationsFromCohort AS&lt;br /&gt;
      SELECT DISTINCT&lt;br /&gt;
             o.id,&lt;br /&gt;
             o.patient_id,&lt;br /&gt;
             o.person_id,&lt;br /&gt;
             o.group_by,&lt;br /&gt;
             o.clinical_effective_date,&lt;br /&gt;
             o.non_core_concept_id AS original_code,&lt;br /&gt;
             SUBSTRING(s.term, 1, 200) AS original_term,&lt;br /&gt;
             o.result_value,&lt;br /&gt;
             o.result_value_units,&lt;br /&gt;
             o.age_years&lt;br /&gt;
     FROM cohort_observations o&lt;br /&gt;
          JOIN snomeds s ON s.snomed_concept_id = o.non_core_concept_id&lt;br /&gt;
     WHERE o.clinical_effective_date IS NOT NULL&lt;br /&gt;
     AND o.clinical_effective_date&lt;br /&gt;
     BETWEEN DATE_SUB(c.pivot_date, INTERVAL 6 MONTH)&lt;br /&gt;
     AND DATE_SUB(c.pivot_date, INTERVAL -6 MONTH);&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
&lt;br /&gt;
-- latest or earliest or ever (so all observations)&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE observationsFromCohort AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
            o.id,&lt;br /&gt;
            o.patient_id,&lt;br /&gt;
            o.person_id,&lt;br /&gt;
            o.group_by,&lt;br /&gt;
            o.clinical_effective_date,&lt;br /&gt;
            o.non_core_concept_id AS original_code,&lt;br /&gt;
            SUBSTRING(s.term, 1, 200) AS original_term,&lt;br /&gt;
            o.result_value,&lt;br /&gt;
            o.result_value_units,&lt;br /&gt;
            o.age_years&lt;br /&gt;
     FROM cohort_observations o&lt;br /&gt;
          JOIN snomeds s ON s.snomed_concept_id = o.non_core_concept_id&lt;br /&gt;
     WHERE o.clinical_effective_date IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE observationsFromCohort ADD INDEX obv_pat_idx(patient_id);&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;This returns a new observations cohort based on the SNOMED code list.&lt;br /&gt;
&lt;br /&gt;
===== Filter Observations by Timeframe =====&lt;br /&gt;
Using the new observations cohort, we pass in the filter type to retrieve the observations based on the timeframe for the analysis, and using the clinical effective date to determine the time intervals. We store the result set into a temporary table. This is illustrated in the code snippet of the filterObservations procedure below: &amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE filteredObservationsV2 as&lt;br /&gt;
  SELECT&lt;br /&gt;
          ob.id,&lt;br /&gt;
          ob.group_by,&lt;br /&gt;
          ob.patient_id,&lt;br /&gt;
          ob.person_id,&lt;br /&gt;
          ob.original_code,&lt;br /&gt;
          ob.original_term,&lt;br /&gt;
          ob.result_value,&lt;br /&gt;
          ob.clinical_effective_date,&lt;br /&gt;
          ob.result_value_units,&lt;br /&gt;
          ob.age_years,&lt;br /&gt;
          ob.rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT o.id,&lt;br /&gt;
                 o.group_by,&lt;br /&gt;
                 o.patient_id,&lt;br /&gt;
                 o.person_id,&lt;br /&gt;
                 o.original_code,&lt;br /&gt;
                 o.original_term,&lt;br /&gt;
                 o.result_value,&lt;br /&gt;
                 o.clinical_effective_date,&lt;br /&gt;
                 o.result_value_units,&lt;br /&gt;
                 o.age_years,&lt;br /&gt;
                 @currank :=&lt;br /&gt;
                    IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
                 @curperson := o.person_id AS cur_person&lt;br /&gt;
          FROM observationsFromCohort o,&lt;br /&gt;
          (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
          WHERE o.result_value IS NOT NULL&lt;br /&gt;
          ORDER BY o.person_id,&lt;br /&gt;
                   o.clinical_effective_date ASC,&lt;br /&gt;
                   o.id ASC  -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
&lt;br /&gt;
  WHERE ob.rnk = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Updating Dataset Table Columns ====&lt;br /&gt;
Using the result set derived from the above step, we can now update the dataset table columns with the appropriate observational values. This is illustrated in the code snippet of the populateCodeDate procedure:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
  CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
      row_id                  INT,&lt;br /&gt;
      group_by                VARCHAR(255),&lt;br /&gt;
      original_code           VARCHAR(20),&lt;br /&gt;
      original_term           VARCHAR(200),&lt;br /&gt;
      clinical_effective_date DATE, PRIMARY KEY(row_id)&lt;br /&gt;
  ) AS&lt;br /&gt;
  SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
         f.group_by,&lt;br /&gt;
         f.original_code,&lt;br /&gt;
         f.original_term,&lt;br /&gt;
         f.clinical_effective_date&lt;br /&gt;
  FROM filteredObservationsV2 f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
              &#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
              col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
              col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
              col, &amp;quot;Date = date_format(f.clinical_effective_date, &#039;%d/%m/%Y&#039;)&lt;br /&gt;
              WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
  PREPARE stmt FROM @sql;&lt;br /&gt;
  EXECUTE stmt;&lt;br /&gt;
  DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;To populate medication datasets, we use a similar approach as above.&lt;br /&gt;
&lt;br /&gt;
= APPENDIX =&lt;br /&gt;
Some supplementary material that maybe helpful when developing SQL query, in particular, working with concept ids.&lt;br /&gt;
&lt;br /&gt;
=== Helper Functions ===&lt;br /&gt;
•          Function to retrieve the description field on the concept table based on the concept id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_concept_desc(p_concept_id int)&lt;br /&gt;
RETURNS VARCHAR(400)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_description VARCHAR(400);&lt;br /&gt;
&lt;br /&gt;
SELECT description INTO l_description&lt;br /&gt;
FROM concept cpt&lt;br /&gt;
WHERE cpt.dbid = p_concept_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_description;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_concept_desc(pat.gender_concept_id) FROM patient pat;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the code field on the concept table based on the concept id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_concept_code;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_concept_code(p_concept_id int)&lt;br /&gt;
RETURNS VARCHAR(20)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_code VARCHAR(20);&lt;br /&gt;
&lt;br /&gt;
SELECT code INTO l_code&lt;br /&gt;
FROM concept cpt&lt;br /&gt;
WHERE cpt.dbid = p_concept_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_code;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_concept_code(epoc.registration_type_concept_id)&lt;br /&gt;
       FROM episode_of_care epoc;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the scheme field on the concept table based on the concept id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_concept_scheme;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_concept_scheme(p_concept_id int)&lt;br /&gt;
RETURNS VARCHAR(50)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_scheme VARCHAR(50);&lt;br /&gt;
&lt;br /&gt;
SELECT scheme INTO l_scheme&lt;br /&gt;
FROM concept cpt&lt;br /&gt;
WHERE cpt.dbid = p_concept_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_scheme;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_concept_scheme(ob.non_core_concept_id) FROM observation ob;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the ods code based on the organization id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_ods;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_ods(p_org_id bigint)&lt;br /&gt;
RETURNS VARCHAR(50)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_ods_code VARCHAR(50);&lt;br /&gt;
&lt;br /&gt;
SELECT ods_code INTO l_ods_code&lt;br /&gt;
FROM organization org&lt;br /&gt;
WHERE org.id = p_org_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_ods_code;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_ods(pat.registered_practice_organization_id) FROM patient pat;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the organization name based on the organization id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_org_name;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_org_name(p_org_id bigint)&lt;br /&gt;
RETURNS VARCHAR(255)&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_org_name VARCHAR(255);&lt;br /&gt;
&lt;br /&gt;
SELECT name INTO l_org_name&lt;br /&gt;
FROM organization org&lt;br /&gt;
WHERE org.id = p_org_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_org_name;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_ods(pat.registered_practice_organization_id) FROM patient pat;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the event type from the description of the concept table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_event_type;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_event_type(p_description VARCHAR(400))&lt;br /&gt;
RETURNS VARCHAR(50)&lt;br /&gt;
NOT DETERMINISTIC NO SQL&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_event VARCHAR(50);&lt;br /&gt;
&lt;br /&gt;
SELECT substr(p_description,instr(p_description,&amp;quot;(&amp;quot;) + 1,&lt;br /&gt;
       instr(p_description,&amp;quot;)&amp;quot;) - instr(p_description,&amp;quot;(&amp;quot;) - 1)&lt;br /&gt;
       INTO l_event;&lt;br /&gt;
&lt;br /&gt;
RETURN l_event;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_event_type(get_concept_desc(ob.core_concept_id)) event_type&lt;br /&gt;
       FROM observation ob;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the latest medication order issue date based on the medication statement id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_lastest_med_issue_date;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_lastest_med_issue_date (p_medication_stmt_id bigint)&lt;br /&gt;
RETURNS DATE&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_date DATE;&lt;br /&gt;
DECLARE l_rnk  INT;&lt;br /&gt;
&lt;br /&gt;
-- get latest issue date&lt;br /&gt;
&lt;br /&gt;
  SELECT&lt;br /&gt;
          med.clinical_effective_date, med.rnk INTO l_date, l_rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            mo.id,&lt;br /&gt;
            mo.medication_statement_id,&lt;br /&gt;
            mo.clinical_effective_date,&lt;br /&gt;
            @currank :=&lt;br /&gt;
            IF(@curmedstmt = mo.medication_statement_id,@currank + 1,1) AS rnk,&lt;br /&gt;
            @curmedstmt := mo.medication_statement_id AS cur_med_stmt&lt;br /&gt;
          FROM medication_order mo,(SELECT @currank := 0, @curmedstmt := 0) r&lt;br /&gt;
          WHERE mo.medication_statement_id = p_medication_stmt_id&lt;br /&gt;
          ORDER BY mo.medication_statement_id DESC,&lt;br /&gt;
                   mo.clinical_effective_date DESC,&lt;br /&gt;
                   mo.id DESC -- latest&lt;br /&gt;
        ) med&lt;br /&gt;
  WHERE med.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
RETURN l_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT DATE_FORMAT(get_lastest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) date_of_last_issue&lt;br /&gt;
       FROM medication_statement med;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the earliest medication order issue date based on the medication statement id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_earliest_med_issue_date;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_earliest_med_issue_date (p_medication_stmt_id bigint)&lt;br /&gt;
RETURNS DATE&lt;br /&gt;
NOT DETERMINISTIC READS SQL DATA&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_date DATE;&lt;br /&gt;
DECLARE l_rnk  INT;&lt;br /&gt;
&lt;br /&gt;
-- get earliest issue date&lt;br /&gt;
&lt;br /&gt;
  SELECT&lt;br /&gt;
          med.clinical_effective_date, med.rnk INTO l_date, l_rnk&lt;br /&gt;
  FROM (&lt;br /&gt;
          SELECT&lt;br /&gt;
            mo.id,&lt;br /&gt;
            mo.medication_statement_id,&lt;br /&gt;
            mo.clinical_effective_date,&lt;br /&gt;
            @currank :=&lt;br /&gt;
            IF(@curmedstmt = mo.medication_statement_id,@currank + 1,1) AS rnk,&lt;br /&gt;
            @curmedstmt := mo.medication_statement_id AS cur_med_stmt&lt;br /&gt;
          FROM medication_order mo,(SELECT @currank := 0, @curmedstmt := 0) r&lt;br /&gt;
          WHERE mo.medication_statement_id = p_medication_stmt_id&lt;br /&gt;
          ORDER BY mo.medication_statement_id ASC,&lt;br /&gt;
                   mo.clinical_effective_date ASC,&lt;br /&gt;
                   mo.id ASC -- earliest&lt;br /&gt;
        ) med&lt;br /&gt;
  WHERE med.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
RETURN l_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT DATE_FORMAT(get_earliest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) issue_date&lt;br /&gt;
       FROM medication_statement med;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the event log last changed date based on the record id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_event_maxdate;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_event_maxdate (p_table_id tinyint, p_record_id bigint)&lt;br /&gt;
RETURNS DATETIME&lt;br /&gt;
READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_max_date DATETIME;&lt;br /&gt;
&lt;br /&gt;
SELECT  MAX(etlg.dt_change) modified_date INTO l_max_date&lt;br /&gt;
FROM event_log etlg&lt;br /&gt;
WHERE etlg.table_id = p_table_id AND record_id = p_record_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_max_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_event_maxdate(2,pat.id) FROM patient pat;&lt;br /&gt;
       Where 2 is the table id for the patient table.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;•          Function to retrieve the event log last changed date for medication order based on the medication statement id:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP FUNCTION IF EXISTS get_event_maxdate_medo;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION get_event_maxdate_medo (p_table_id tinyint, p_record_id bigint)&lt;br /&gt;
RETURNS datetime&lt;br /&gt;
READS SQL DATA&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DECLARE l_max_date datetime;&lt;br /&gt;
&lt;br /&gt;
SELECT MAX(etlg.dt_change) INTO l_max_date&lt;br /&gt;
FROM event_log etlg JOIN medication_order mo&lt;br /&gt;
ON mo.medication_statement_id = p_record_id&lt;br /&gt;
AND mo.id = etlg.record_id&lt;br /&gt;
WHERE etlg.table_id = p_table_id;&lt;br /&gt;
&lt;br /&gt;
RETURN l_max_date;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
Usage: SELECT get_event_maxdate_medo (9,med.id) FROM medication_statement med &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQL Statement Examples ===&lt;br /&gt;
&#039;&#039;&#039;Patient Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     pat.id patient_id,&lt;br /&gt;
     get_ods(pat.registered_practice_organization_id) source_organization,&lt;br /&gt;
     IF (pat.date_of_death IS NULL,&lt;br /&gt;
        TIMESTAMPDIFF(YEAR,pat.date_of_birth,CURDATE()),&lt;br /&gt;
        TIMESTAMPDIFF(YEAR,pat.date_of_birth,pat.date_of_death)) age,&lt;br /&gt;
     get_concept_desc(pat.gender_concept_id) sex,&lt;br /&gt;
     DATE_FORMAT(epoc.startregdate,&amp;quot;%Y%m%d&amp;quot;) start_regdate,&lt;br /&gt;
     get_concept_code(epoc.registration_type_concept_id)  reg_status,&lt;br /&gt;
     pat.nhs_number nhsnumber,&lt;br /&gt;
     pad.postcode,&lt;br /&gt;
     DATE_FORMAT(pat.date_of_birth,&amp;quot;%Y%m%d&amp;quot;) date_of_birth,&lt;br /&gt;
     epoc.usualgp,&lt;br /&gt;
     pat.first_names forename,&lt;br /&gt;
     pat.last_name surname,&lt;br /&gt;
     get_concept_desc(pat.ethnic_code_concept_id) ethnic_category,&lt;br /&gt;
     DATE_FORMAT(pat.date_of_death,&amp;quot;%Y%m%d&amp;quot;) date_of_death,&lt;br /&gt;
     DATE_FORMAT(epoc.endregdate,&amp;quot;%Y%m%d&amp;quot;) end_reg_date,&lt;br /&gt;
     get_optout_status(pat.id, pat.registered_practice_organization_id) optout_status,&lt;br /&gt;
     pad.address_line_1,&lt;br /&gt;
     pad.address_line_2,&lt;br /&gt;
     pad.address_line_3,&lt;br /&gt;
     pad.address_line_4,&lt;br /&gt;
     pad.city,&lt;br /&gt;
     NULLIF(GREATEST(COALESCE(get_event_maxdate(2,pat.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate(20,pad.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate(6,epoc.id),0)),0) last_modified_date&lt;br /&gt;
FROM patient pat&lt;br /&gt;
LEFT JOIN patient_address pad ON pat.current_address_id = pad.id AND pat.id = pad.patient_id&lt;br /&gt;
LEFT JOIN&lt;br /&gt;
  (SELECT&lt;br /&gt;
          epc.id,&lt;br /&gt;
          epc.organization_id,&lt;br /&gt;
          epc.patient_id,&lt;br /&gt;
          epc.registration_type_concept_id,&lt;br /&gt;
          epc.registration_status_concept_id,&lt;br /&gt;
          epc.date_registered startregdate,&lt;br /&gt;
          epc.date_registered_end endregdate,&lt;br /&gt;
          epc.usual_gp_practitioner_id,&lt;br /&gt;
          prac.name usualgp&lt;br /&gt;
  FROM episode_of_care epc LEFT JOIN practitioner prac&lt;br /&gt;
  ON prac.id = epc.usual_gp_practitioner_id&lt;br /&gt;
  AND prac.organization_id = epc.organization_id) epoc&lt;br /&gt;
  ON epoc.patient_id = pat.id&lt;br /&gt;
  AND epoc.organization_id = pat.registered_practice_organization_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Prescription Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     med.id event_id,&lt;br /&gt;
     get_ods(med.organization_id) source_organization,&lt;br /&gt;
     med.patient_id patient_id,&lt;br /&gt;
     pat.nhs_number nhs_number,&lt;br /&gt;
     get_concept_desc(med.authorisation_type_concept_id)  issue_type,&lt;br /&gt;
     DATE_FORMAT(get_earliest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) issue_date,&lt;br /&gt;
     DATE_FORMAT(get_lastest_med_issue_date(med.id),&amp;quot;%Y%m%d&amp;quot;) date_of_last_issue,&lt;br /&gt;
     DATE_FORMAT(med.clinical_effective_date,&amp;quot;%Y%m%d&amp;quot;) recorded_date,&lt;br /&gt;
     get_concept_code(med.core_concept_id) dmdcode,&lt;br /&gt;
     med.dose dosage,&lt;br /&gt;
     med.quantity_value quantity,&lt;br /&gt;
     med.quantity_unit quantity_unit,&lt;br /&gt;
     get_concept_desc(med.core_concept_id) dmd_description,&lt;br /&gt;
     NULLIF(GREATEST(COALESCE(get_event_maxdate(10,med.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate_medo(9,med.id),0)),0) last_modified_date&lt;br /&gt;
FROM medication_statement med JOIN patient pat ON med.patient_id = pat.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Clinical Event Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     ob.id event_id,&lt;br /&gt;
     get_ods(ob.organization_id) source_organization,&lt;br /&gt;
     ob.patient_id,&lt;br /&gt;
     pat.nhs_number nhs_number,&lt;br /&gt;
     DATE_FORMAT(ob.clinical_effective_date,&amp;quot;%Y%m%d&amp;quot;) effective_date,&lt;br /&gt;
     get_concept_code(ob.non_core_concept_id) original_code,&lt;br /&gt;
     get_concept_desc(ob.non_core_concept_id) original_term,&lt;br /&gt;
     get_concept_desc(get_concept_scheme(ob.non_core_concept_id)) original_code_scheme,&lt;br /&gt;
     ob.result_value result_value,&lt;br /&gt;
     CASE&lt;br /&gt;
     WHEN ob.is_problem = 1 AND ob.is_review = 0 THEN &#039;N - New&#039;&lt;br /&gt;
     WHEN ob.is_problem = 1 AND ob.is_review = 1 THEN &#039;F - Follow Up&#039;&lt;br /&gt;
     ELSE &#039;O - Other&#039;&lt;br /&gt;
     END AS episode_type,&lt;br /&gt;
     prac.role_desc hcp_role,&lt;br /&gt;
     enc.type encounter_type,&lt;br /&gt;
     get_concept_code(ob.core_concept_id) snomed_concept_id,&lt;br /&gt;
     get_concept_desc(ob.core_concept_id) snomed_term,&lt;br /&gt;
     ob.parent_observation_id parent_event,&lt;br /&gt;
     get_ods(enc.organization_id) event_location,&lt;br /&gt;
     get_org_name(enc.organization_id) location_name,&lt;br /&gt;
     enc.appointment_id appointment_slot_id,&lt;br /&gt;
     get_event_type(get_concept_desc(ob.core_concept_id)) event_type,&lt;br /&gt;
    NULLIF(GREATEST(COALESCE(get_event_maxdate(11,ob.id),0),&lt;br /&gt;
                    COALESCE(get_event_maxdate(5,enc.id),0),&lt;br /&gt;
                    COALESCE(get_event_maxdate(13,prac.id),0)),0) last_modified_date&lt;br /&gt;
FROM observation ob JOIN patient pat ON ob.patient_id = pat.id&lt;br /&gt;
LEFT JOIN practitioner prac ON prac.id = ob.practitioner_id&lt;br /&gt;
LEFT JOIN encounter enc ON enc.id = ob.encounter_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Patient Registration History Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     get_ods(epoc.organization_id) source_origanization,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     get_concept_code(epoc.registration_status_concept_id) reg_status,&lt;br /&gt;
     get_concept_desc(epoc.registration_status_concept_id) reg_status_desc,&lt;br /&gt;
     get_concept_code(epoc.registration_type_concept_id) registration_type,&lt;br /&gt;
     get_concept_desc(epoc.registration_type_concept_id) registration_type_desc,&lt;br /&gt;
     DATE_FORMAT(epoc.date_registered,&amp;quot;%Y%m%d&amp;quot;) date_registered,&lt;br /&gt;
     DATE_FORMAT(epoc.date_registered_end,&amp;quot;%Y%m%d&amp;quot;) date_registered_end,&lt;br /&gt;
     epoc.id sequence_number,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(6,epoc.id),0),0)  last_modified_date&lt;br /&gt;
FROM episode_of_care epoc JOIN patient pat ON epoc.patient_id = pat.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Patient Address Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     padr.id address_id,&lt;br /&gt;
     padr.address_line_1 address1,&lt;br /&gt;
     padr.address_line_2 address2,&lt;br /&gt;
     padr.address_line_3 address3,&lt;br /&gt;
     padr.address_line_4 address4,&lt;br /&gt;
     padr.city address5,&lt;br /&gt;
     padr.postcode postcode,&lt;br /&gt;
     parn.uprn,&lt;br /&gt;
     DATE_FORMAT(padr.start_date,&amp;quot;%Y%m%d&amp;quot;) start_date,&lt;br /&gt;
     DATE_FORMAT(padr.end_date,&amp;quot;%Y%m%d&amp;quot;) end_date,&lt;br /&gt;
     padr.lsoa_2001_code,&lt;br /&gt;
     padr.lsoa_2011_code,&lt;br /&gt;
     padr.msoa_2001_code,&lt;br /&gt;
     padr.msoa_2011_code,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     parn.abp_address_number matched_address_1,&lt;br /&gt;
     parn.abp_address_street matched_address_2,&lt;br /&gt;
     parn.abp_address_locality matched_address_3,&lt;br /&gt;
     parn.abp_address_town matched_address_4,&lt;br /&gt;
     parn.abp_address_postcode matched_postcode,&lt;br /&gt;
     parn.classification,&lt;br /&gt;
     parn.abp_address_organization business_name,&lt;br /&gt;
     parn.match_date uprn_match_date,&lt;br /&gt;
     parn.status uprn_status,&lt;br /&gt;
     parn.latitude,&lt;br /&gt;
     parn.longitude,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(20,padr.id),0),0)  last_modified_date&lt;br /&gt;
FROM patient_address padr JOIN patient pat ON padr.patient_id = pat.id&lt;br /&gt;
LEFT JOIN patient_address_match parn ON padr.id = parn.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Appointment Slot Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     appt.id slot_id,&lt;br /&gt;
     get_ods(appt.organization_id) source_origanization,&lt;br /&gt;
     appt.schedule_id session_id,&lt;br /&gt;
     DATE_FORMAT(appt.start_date,&amp;quot;%Y%m%d %T&amp;quot;) start_time,&lt;br /&gt;
     appt.planned_duration planned_duration,&lt;br /&gt;
     appt.actual_duration actual_duration,&lt;br /&gt;
     get_concept_desc(appt.appointment_status_concept_id) status,&lt;br /&gt;
     DATE_FORMAT(appt.date_time_sent_in,&amp;quot;%Y%m%d %T&amp;quot;) actual_start_time,&lt;br /&gt;
     appt.patient_wait wait_time,&lt;br /&gt;
     DATE_FORMAT(appt.cancelled_date,&amp;quot;%Y%m%d %T&amp;quot;) date_cancelled,&lt;br /&gt;
     DATE_FORMAT(appt.date_time_left,&amp;quot;%Y%m%d %T&amp;quot;) time_left,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(18,appt.id),0),0)  last_modified_date&lt;br /&gt;
FROM appointment appt JOIN patient pat ON appt.patient_id = pat.id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Encounter Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     enc.id encounter_id,&lt;br /&gt;
     get_ods(enc.organization_id) source_organization,&lt;br /&gt;
     pat.nhs_number,&lt;br /&gt;
     DATE_FORMAT(enc.clinical_effective_date,&amp;quot;%Y%m%d %T&amp;quot;) encounter_startdate,&lt;br /&gt;
     DATE_FORMAT(enc.end_date,&amp;quot;%Y%m%d %T&amp;quot;) encounter_enddate,&lt;br /&gt;
     get_concept_desc(enc.non_core_concept_id) original_type,&lt;br /&gt;
     prac.role_desc hcp_role,&lt;br /&gt;
     enc.type encounter_type,&lt;br /&gt;
     get_ods(enc.service_provider_organization_id) event_organization,&lt;br /&gt;
     enc.appointment_id appointment_slot_id,&lt;br /&gt;
    NULLIF(GREATEST(COALESCE(get_event_maxdate(5,enc.id),0),&lt;br /&gt;
                    COALESCE(get_event_maxdate(13,prac.id),0)),0) last_modified_date&lt;br /&gt;
FROM encounter enc JOIN patient pat ON enc.patient_id = pat.id&lt;br /&gt;
LEFT JOIN practitioner prac ON prac.id = enc.practitioner_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Organization Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     org.id organization_id,&lt;br /&gt;
     org.ods_code,&lt;br /&gt;
     org.name organization_name,&lt;br /&gt;
     org.type_code,&lt;br /&gt;
     org.type_desc,&lt;br /&gt;
     org.postcode,&lt;br /&gt;
     org.parent_organization_id,&lt;br /&gt;
     NULLIF(COALESCE(get_event_maxdate(12,org.id),0),0)  last_modified_date&lt;br /&gt;
FROM organization org;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Appointment Session Information&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT&lt;br /&gt;
     sch.id session_id,&lt;br /&gt;
     sch.name session_name,&lt;br /&gt;
     get_ods(sch.organization_id) source_organization,&lt;br /&gt;
     prac.role_desc hpc_role,&lt;br /&gt;
     DATE_FORMAT(sch.start_date,&amp;quot;%Y%m%d %T:%f&amp;quot;) start_time,&lt;br /&gt;
     sch.type session_type,&lt;br /&gt;
     sch.location location,&lt;br /&gt;
     NULLIF(GREATEST(COALESCE(get_event_maxdate(17,sch.id),0),&lt;br /&gt;
                     COALESCE(get_event_maxdate(13,prac.id),0)),0) last_modified_date&lt;br /&gt;
FROM schedule sch JOIN practitioner prac ON prac.id = sch.practitioner_id;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>AndreHui</name></author>
	</entry>
	<entry>
		<id>https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_1)&amp;diff=1295</id>
		<title>Remote Subscriber Database (RSD) SQL guide (Compass 1)</title>
		<link rel="alternate" type="text/html" href="https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_1)&amp;diff=1295"/>
		<updated>2020-06-09T11:28:45Z</updated>

		<summary type="html">&lt;p&gt;AndreHui: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This article describes the steps needed to create, and analyse, patient data selections from the Discovery database.&lt;br /&gt;
&lt;br /&gt;
Datasets are needed to add patient data to a report. Each dataset represents the results gained from running a combination of SQL commands and stored procedures on a data source.&lt;br /&gt;
&lt;br /&gt;
The following diagram shows the algorithm that is followed to generate these datasets:&lt;br /&gt;
&lt;br /&gt;
[[File:Dataset algorithm.jpg|600x600px|The algorithm followed to generate datasets]]&lt;br /&gt;
&lt;br /&gt;
== Patient cohort ==&lt;br /&gt;
&lt;br /&gt;
=== Creating the patient cohort ===&lt;br /&gt;
The first step, when a list of patients marked for observational study is received, is to create a patient cohort. Patient data usually comes into the DDS in the form of pseudonymised personal data. Additional information about the patient can be obtained from the Discovery database using the pseudo identifier or the pseudo NHS identifier.&lt;br /&gt;
&lt;br /&gt;
The following entity diagram shows the relationships between patient and organisation:&lt;br /&gt;
&lt;br /&gt;
[[File:General patient cohort.jpg|500px|General patient cohort]]&lt;br /&gt;
&lt;br /&gt;
Patients are linked to the Discovery database using pseudo patient identifiers. However, if only pseudo NHS numbers are provided, the corresponding pseudo identifiers can be derived from the &#039;&#039;&#039;link distributor&#039;&#039;&#039; table. Patients are filtered by organisation ODS codes to ensure that only the patients of the required GP practices are selected.&lt;br /&gt;
&lt;br /&gt;
=== Building the patient cohort SQL with pseudo NHS numbers ===&lt;br /&gt;
The following example SQL shows how a patient cohort is built using pseudo NHS numbers:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort AS&lt;br /&gt;
SELECT&lt;br /&gt;
       p.id                 AS patient_id,&lt;br /&gt;
       p.pseudo_id          AS group_by,&lt;br /&gt;
       p.person_id          AS person_id,&lt;br /&gt;
       p.organization_id    AS organization_id,&lt;br /&gt;
       n.pseudo_nhsnumber   AS pseudo_nhsnumber,&lt;br /&gt;
       p.age_years&lt;br /&gt;
FROM pseudonhsnumbers n&lt;br /&gt;
JOIN link_distributor l&lt;br /&gt;
  ON n.pseudo_nhsnumber = l.target_skid AND l.target_salt_key_name = &#039;EGH&#039;&lt;br /&gt;
JOIN patient p ON p.pseudo_id = l.source_skid&lt;br /&gt;
JOIN organization org ON p.organization_id = org.id&lt;br /&gt;
WHERE EXISTS (SELECT &#039;x&#039; FROM ccg_codes ccgs&lt;br /&gt;
     WHERE ccgs.parent IN (&#039;City &amp;amp; Hackney CCG&#039;,&#039;Newham CCG&#039;,&#039;Tower Hamlets CCG&#039;,&#039;Waltham Forest CCG&#039;) &lt;br /&gt;
     AND ccgs.local_id = org.ods_code);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Sometimes, the cohort is created with filtered observations.&lt;br /&gt;
&lt;br /&gt;
The example below filters patients who are registered, alive, and have had diabetes. They belong to the age 12 years and over. Two additional tables are introduced for the query: &#039;&#039;&#039;observation&#039;&#039;&#039;(patients who have had diabetes) and &#039;&#039;&#039;episode of care &#039;&#039;&#039;(patients who are registered and have the appropriate registration type for this study).&lt;br /&gt;
&lt;br /&gt;
[[File:Patient observationepisode of care.jpg|500px|Cohorts with filtered observations]]&lt;br /&gt;
&lt;br /&gt;
Patients can be selected with a specific set of observations using a lookup table of relevant&amp;amp;nbsp;SNOMED codes.&amp;lt;br /&amp;gt;Some patients prefer to withhold consent and are therefore excluded from the cohort;&amp;amp;nbsp;the relevant SNOMED consent code can be checked in the observation table.&amp;lt;br /&amp;gt;Patients are only selected usually if they have registration type 2&amp;amp;nbsp;(Regular/GMS).&amp;lt;br /&amp;gt;Sometimes patients are also selected based on their age in years.&lt;br /&gt;
&lt;br /&gt;
=== Building patient cohort SQL with filtered observations ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
         p.id                                 AS patient_id,&lt;br /&gt;
         p.person_id                          AS person_id,&lt;br /&gt;
         p.pseudo_id                          AS group_by,&lt;br /&gt;
         org.id                               AS organization_id,&lt;br /&gt;
         org.ods_code                         AS practice_code,&lt;br /&gt;
         org.name                             AS practice_name,&lt;br /&gt;
         e.date_registered                    AS registered_date,     &lt;br /&gt;
         p.date_of_death                      AS date_of_death&lt;br /&gt;
  FROM observation o&lt;br /&gt;
    JOIN organization org ON org.id = o.organization_id&lt;br /&gt;
    JOIN patient p ON p.id = o.patient_id&lt;br /&gt;
    JOIN episode_of_care e ON e.patient_id = o.patient_id&lt;br /&gt;
  WHERE EXISTS &lt;br /&gt;
  (SELECT &#039;x&#039;FROM snomed_codes s&lt;br /&gt;
   WHERE s.group_id = 1 AND s.snomed_id = o.snomed_concept_id)&lt;br /&gt;
  AND NOT EXISTS &lt;br /&gt;
  (SELECT &#039;x&#039;FROM snomed_codes s&lt;br /&gt;
   WHERE s.group_id = 2 AND s.snomed_id = o.snomed_concept_id)&lt;br /&gt;
  AND org.ods_code IN &lt;br /&gt;
  (SELECT el.local_id FROM elccg_codes el&lt;br /&gt;
   WHERE el.parent IN (&#039;City &amp;amp; Hackney CCG&#039;,&#039;Newham CCG&#039;,&lt;br /&gt;
                       &#039;Tower Hamlets CCG&#039;,&#039;Waltham Forest CCG&#039;))&lt;br /&gt;
  AND p.date_of_death IS NULL&lt;br /&gt;
  AND e.registration_type_id = 2 AND e.date_registered &amp;lt;= now()&lt;br /&gt;
  AND (e.date_registered_end &amp;gt; now() or e.date_registered_end IS NULL) AND (p.age_years &amp;gt;= 12);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Observation cohort ==&lt;br /&gt;
&lt;br /&gt;
=== Creating the observation cohort ===&lt;br /&gt;
&lt;br /&gt;
Observation&amp;amp;nbsp;cohort are created to improve query performance;&amp;amp;nbsp;instead of scanning through the entire observation table, a sub-set of the observation table is created, based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
=== Building the observation cohort SQL&amp;amp;nbsp; ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_observations AS&lt;br /&gt;
	SELECT DISTINCT&lt;br /&gt;
           o.id,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           cr.group_by,&lt;br /&gt;
           cr.pivot_date,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.snomed_concept_id AS original_code,&lt;br /&gt;
           SUBSTRING(o.original_term, 1, 200) AS original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           cr.age_years&lt;br /&gt;
	FROM observation o JOIN cohort cr&lt;br /&gt;
	ON o.person_id = cr.person_id&lt;br /&gt;
	AND o.organization_id = cr.organization_id&lt;br /&gt;
	AND o.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX obs_ix ON cohort_observations(original_code);&lt;br /&gt;
CREATE INDEX obs_grpby_ix ON cohort_observations(group_by);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Medications cohort ==&lt;br /&gt;
&lt;br /&gt;
=== Creating the medications cohort ===&lt;br /&gt;
&lt;br /&gt;
Medications cohort are created to improve query performance;&amp;amp;nbsp;instead of scanning through the entire observation table, a sub-set of the medication statement table is created, based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
[[File:Patient, medication statement and organization.png|Patient, medication statement and organisation]]&lt;br /&gt;
&lt;br /&gt;
=== Building the medication cohort SQL ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_medications AS&lt;br /&gt;
SELECT DISTINCT&lt;br /&gt;
       m.id,&lt;br /&gt;
       m.dmd_id AS original_code,&lt;br /&gt;
       m.person_id,&lt;br /&gt;
       m.patient_id,&lt;br /&gt;
       cr.group_by,&lt;br /&gt;
       cr.pivot_date,&lt;br /&gt;
       SUBSTRING(m.original_term, 1, 200) AS original_term,&lt;br /&gt;
       m.clinical_effective_date,&lt;br /&gt;
       m.cancellation_date&lt;br /&gt;
FROM medication_statement m JOIN cohort cr&lt;br /&gt;
ON m.organization_id = cr.organization_id&lt;br /&gt;
AND m.person_id = cr.person_id&lt;br /&gt;
AND m.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_ix ON cohort_medications(original_code);&lt;br /&gt;
CREATE INDEX med_grpby_ix ON cohort_medications(group_by);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other cohorts ==&lt;br /&gt;
&lt;br /&gt;
=== Creating other cohorts ===&lt;br /&gt;
&lt;br /&gt;
This step can apply to any large tables to support improved query performance. For example, the encounter_raw table is a good candidate to create a separate cohort for SQL query; you can query this table if you want information such as the number of face to face encounters, or the number of home visits.&lt;br /&gt;
&lt;br /&gt;
[[File:Encounter.png|Encounter raw entity]]&lt;br /&gt;
&lt;br /&gt;
=== Building the encounter raw cohort SQL ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_encounter_raw AS&lt;br /&gt;
SELECT r.person_id,&lt;br /&gt;
       r.patient_id,&lt;br /&gt;
       r.clinical_effective_date,&lt;br /&gt;
       SUBSTRING(UPPER(r.fhir_original_term),1,200) fhir_original_term,&lt;br /&gt;
       r.organization_id&lt;br /&gt;
FROM encounter_raw r&lt;br /&gt;
JOIN cohort c ON r.patient_id = c.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX encounter_term_idx ON cohort_encounter_raw(fhir_original_term);&lt;br /&gt;
CREATE INDEX encounter_person_idx ON cohort_encounter_raw(person_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building datasets ==&lt;br /&gt;
This involves creating a number of tables in the database.&lt;br /&gt;
&lt;br /&gt;
=== Patient demographic dataset ===&lt;br /&gt;
A typical patient demographic dataset includes the following information:&lt;br /&gt;
&lt;br /&gt;
[[File:Patient demographic dataset.png|Patient demographic dataset]] &lt;br /&gt;
&lt;br /&gt;
This information is translated into a &#039;create table&#039; script, which is then run to create the dataset table in the database.&lt;br /&gt;
&lt;br /&gt;
=== Create table script for a patient demographic dataset table ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE demographicsDataset (&lt;br /&gt;
	ExtractDate           DATETIME NULL,&lt;br /&gt;
	Pseudo_id             VARCHAR(255) NULL,&lt;br /&gt;
	Pseudo_NHSNumber      VARCHAR(255) NULL,&lt;br /&gt;
	Gender                VARCHAR(50) NULL,&lt;br /&gt;
	Age                   VARCHAR(50) NULL,&lt;br /&gt;
	DateOfBirth           DATE NULL,&lt;br /&gt;
	EthnicityLCode        VARCHAR(50) NULL,&lt;br /&gt;
	EthnicityLTerm        VARCHAR(200) NULL,&lt;br /&gt;
	BirthCountryLCode     VARCHAR(50) NULL,&lt;br /&gt;
	BirthCountryLTerm     VARCHAR(200) NULL,&lt;br /&gt;
	RegistrationStart     DATE NULL,&lt;br /&gt;
	RegistrationEnd       DATE NULL,&lt;br /&gt;
	IMD2010               VARCHAR(50) NULL,&lt;br /&gt;
	LSOA2011              VARCHAR(50) NULL,&lt;br /&gt;
	PracticeODSCode       VARCHAR(50) NULL,&lt;br /&gt;
	PracticeODSName       VARCHAR(255) NULL,&lt;br /&gt;
	CCGName               VARCHAR(100) NULL,&lt;br /&gt;
	YearOfDeath           INT(4) NULL,&lt;br /&gt;
	F2fVisits_Total       INT(11) DEFAULT 0,&lt;br /&gt;
	F2fVisits_1year       INT(11) DEFAULT 0,&lt;br /&gt;
	F2fVisits_5years      INT(11) DEFAULT 0&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE demographicsDataset ADD INDEX demo_pseudoid_idx (pseudo_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The dataset is then populated  with pseudo identifiers from the patient cohort.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
INSERT INTO demographicsDataset (pseudo_id, extractdate) &lt;br /&gt;
SELECT DISTINCT group_by, now() FROM cohort;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Separate update SQL queries then populate the columns of the dataset table. For example:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_age;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_age (&lt;br /&gt;
	row_id INT,&lt;br /&gt;
	group_by VARCHAR(255),&lt;br /&gt;
	age_years INT,&lt;br /&gt;
	lsoa_code VARCHAR(50), PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
	c.group_by,&lt;br /&gt;
	p.age_years,&lt;br /&gt;
	p.lsoa_code&lt;br /&gt;
FROM patient p JOIN cohort c ON p.pseudo_id = c.group_by, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS&lt;br /&gt;
(SELECT row_id from qry_age WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN qry_age q ON d.pseudo_id = q.group_by&lt;br /&gt;
	SET d.Age = q.age_years,&lt;br /&gt;
	d.LSOA2011 = q.lsoa_code&lt;br /&gt;
	WHERE q.row_id &amp;gt; @row_id AND q.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In the following example, the age and LSOA code are derived from the Patient table. The demographic dataset table is updated with those values when there is a match on the pseudo identifier. If the update is large, it might be possible to optimise the update process by using batches; for example to add IMD2010:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_imd;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_imd (&lt;br /&gt;
	row_id INT,&lt;br /&gt;
	pseudo_id VARCHAR(255),&lt;br /&gt;
	IMD2010 DECIMAL(5,3), PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
	d.pseudo_id,&lt;br /&gt;
	lso.imd_score&lt;br /&gt;
FROM demographicsDataset d JOIN lsoa_lookup lso ON d.LSOA2011 = lso.lsoa_code, (SELECT @row_no := 0) t&lt;br /&gt;
WHERE d.lsoa2011 IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS&lt;br /&gt;
(SELECT row_id from qry_imd WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN qry_imd i ON d.pseudo_id = i.pseudo_id&lt;br /&gt;
	SET d.IMD2010 = i.imd_score&lt;br /&gt;
	WHERE i.row_id &amp;gt; @row_id AND i.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Add registration start date, registration end date, practice ODS code, practice ODS name:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS reg_sort;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_reg;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_reg AS&lt;br /&gt;
SELECT 	c.group_by,&lt;br /&gt;
		c.person_id,&lt;br /&gt;
		e.date_registered,&lt;br /&gt;
		e.date_registered_end,&lt;br /&gt;
		o.name,&lt;br /&gt;
		o.ods_code,&lt;br /&gt;
		g.parent&lt;br /&gt;
FROM cohort c&lt;br /&gt;
JOIN episode_of_care e ON e.person_id = c.person_id&lt;br /&gt;
JOIN organization o ON o.id = e.organization_id&lt;br /&gt;
JOIN ccg_codes g ON g.local_id = o.ods_code;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE reg_sort (&lt;br /&gt;
	row_id                  INT,&lt;br /&gt;
	group_by                VARCHAR(255),&lt;br /&gt;
	person_id               BIGINT,&lt;br /&gt;
	date_registered         DATE,&lt;br /&gt;
	date_regiostered_end    DATE,&lt;br /&gt;
	ode_code                VARCHAR(50),&lt;br /&gt;
	name                    VARCHAR(255),&lt;br /&gt;
	parent                  VARCHAR(100),&lt;br /&gt;
	rnk                     INT, PRIMARY KEY(row_id)) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
	a.group_by,&lt;br /&gt;
	a.person_id,&lt;br /&gt;
	a.date_registered,&lt;br /&gt;
	a.date_registered_end,&lt;br /&gt;
	a.ods_code,&lt;br /&gt;
	a.name,&lt;br /&gt;
	a.parent,&lt;br /&gt;
	a.rnk&lt;br /&gt;
FROM (SELECT  q.group_by,&lt;br /&gt;
              q.person_id,&lt;br /&gt;
              q.date_registered,&lt;br /&gt;
              q.date_registered_end,&lt;br /&gt;
              q.name,&lt;br /&gt;
              q.ods_code,&lt;br /&gt;
              q.parent,&lt;br /&gt;
              @currank := IF(@curperson = q.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
              @curperson := q.person_id AS cur_person&lt;br /&gt;
      FROM qry_reg q, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
      ORDER BY q.person_id, q.date_registered DESC ) a, (SELECT @row_no := 0) t&lt;br /&gt;
WHERE a.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from reg_sort&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN reg_sort reg ON d.pseudo_id = reg.group_by&lt;br /&gt;
	SET d.RegistrationStart = reg.date_registered,&lt;br /&gt;
		d.RegistrationEnd = reg.date_registered_end,&lt;br /&gt;
		d.PracticeODSCode = reg.ods_code,&lt;br /&gt;
		d.PracticeODSName = reg.name,&lt;br /&gt;
		d.CCGName = reg.parent&lt;br /&gt;
	WHERE reg.row_id &amp;gt; @row_id&lt;br /&gt;
	AND reg.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In the above example, two temporary tables are created; the first to hold the registration data, and the second to select patient records that have the latest registration date. Add the total number of face to face visits:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS noVisitsTotal;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE noVisitsTotal (&lt;br /&gt;
	row_id INT,&lt;br /&gt;
	group_by VARCHAR(255),&lt;br /&gt;
	visits INT, PRIMARY KEY(row_id) ) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
       b.group_by,&lt;br /&gt;
       b.visits&lt;br /&gt;
FROM (SELECT cr.group_by AS group_by,&lt;br /&gt;
             COUNT(DISTINCT e.clinical_effective_date) AS visits&lt;br /&gt;
      FROM cohort_encounter_raw e JOIN cohort cr&lt;br /&gt;
      ON e.person_id = cr.person_id&lt;br /&gt;
      WHERE EXISTS (SELECT &#039;x&#039; FROM f2fEncounters s&lt;br /&gt;
                    WHERE s.term = e.fhir_original_term)&lt;br /&gt;
                    AND e.clinical_effective_date IS NOT NULL&lt;br /&gt;
                    AND e.fhir_original_term IS NOT NULL&lt;br /&gt;
                    GROUP BY cr.group_by) b, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from noVisitsTotal&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN noVisitsTotal nvt ON d.pseudo_id = nvt.group_by&lt;br /&gt;
	SET d.F2fVisits_Total = nvt.visits&lt;br /&gt;
	WHERE nvt.row_id &amp;gt; @row_id AND nvt.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Face to face visits are identified by the FHIR original terms in the Encounter table. The count distinct is used on the clinical effective date to identify the number of distinct visits. For example, add the total number of face to face visits in the last 1 year:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS lastyearvisits;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE lastyearvisits (&lt;br /&gt;
  row_id INT,&lt;br /&gt;
  group_by VARCHAR(255),&lt;br /&gt;
  visits INT, PRIMARY KEY(row_id) ) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
       b.group_by,&lt;br /&gt;
       b.visits&lt;br /&gt;
FROM (SELECT cr.group_by AS group_by,&lt;br /&gt;
             COUNT(DISTINCT e.clinical_effective_date) AS visits&lt;br /&gt;
      FROM cohort_encounter_raw e JOIN cohort cr&lt;br /&gt;
      ON e.person_id = cr.person_id&lt;br /&gt;
      WHERE EXISTS (SELECT &#039;x&#039; FROM f2fEncounters s&lt;br /&gt;
                    WHERE s.term = e.fhir_original_term)&lt;br /&gt;
      AND e.clinical_effective_date &amp;gt; DATE_SUB(now(), INTERVAL 12 MONTH) -- last 1 yr&lt;br /&gt;
      AND e.clinical_effective_date IS NOT NULL&lt;br /&gt;
      AND e.fhir_original_term IS NOT NULL&lt;br /&gt;
      GROUP BY cr.group_by) b, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from lastyearvisits&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  UPDATE demographicsDataset d&lt;br /&gt;
  JOIN lastyearvisits lyr ON d.pseudo_id = lyr.group_by&lt;br /&gt;
  SET d.F2fVisits_1year = lyr.visits&lt;br /&gt;
  WHERE lyr.row_id &amp;gt; @row_id AND lyr.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The clinical effective date is used to determine the time interval.&amp;lt;br /&amp;gt;For example, add the total number of face to face visits for the last 5 years:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS lastfiveyearvisits;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE lastfiveyearvisits (&lt;br /&gt;
  row_id INT,&lt;br /&gt;
  group_by VARCHAR(255),&lt;br /&gt;
  visits INT, PRIMARY KEY(row_id) ) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
       b.group_by,&lt;br /&gt;
       b.visits&lt;br /&gt;
FROM (SELECT cr.group_by AS group_by,&lt;br /&gt;
             COUNT(DISTINCT e.clinical_effective_date) AS visits&lt;br /&gt;
      FROM cohort_encounter_raw e JOIN cohort cr &lt;br /&gt;
      ON e.person_id = cr.person_id&lt;br /&gt;
      WHERE EXISTS (SELECT &#039;x&#039; FROM f2fEncounters s&lt;br /&gt;
                    WHERE s.term = e.fhir_original_term)&lt;br /&gt;
      AND e.clinical_effective_date &amp;gt; DATE_SUB(now(), INTERVAL 60 MONTH) -- last 5 yrs&lt;br /&gt;
      AND e.clinical_effective_date IS NOT NULL&lt;br /&gt;
      AND e.fhir_original_term IS NOT NULL&lt;br /&gt;
      GROUP BY cr.group_by) b, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from lastfiveyearvisits&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  UPDATE demographicsDataset d&lt;br /&gt;
  JOIN lastfiveyearvisits lfyr ON d.pseudo_id = lfyr.group_by&lt;br /&gt;
  SET d.F2fVisits_5years = lfyr.visits&lt;br /&gt;
  WHERE lfyr.row_id &amp;gt; @row_id AND lfyr.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Note:&#039;&#039;&#039; the patient&#039;s name, age, and date of birth are embedded in the pseudo identifier string that needs to be de-anonymised before it is sent to the recipient.&lt;br /&gt;
&lt;br /&gt;
=== Patient observations dataset ===&lt;br /&gt;
A typical patient observations dataset includes the following information: &lt;br /&gt;
&lt;br /&gt;
[[File:Patient Observations Dataset.png]]&lt;br /&gt;
&lt;br /&gt;
A typical dataset can contain 100 to 200 fields, and usually there are several datasets to deliver.&lt;br /&gt;
&lt;br /&gt;
=== Create table script for a patient diagnosis dataset table ===&lt;br /&gt;
In the following example the create table script produces the patient diagnoses dataset table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS diagnoses2dataset;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE diagnoses2dataset (&lt;br /&gt;
  Pseudo_id                              VARCHAR(255) NULL,&lt;br /&gt;
  Pseudo_NHSNumber                       VARCHAR(255) NULL,&lt;br /&gt;
  AsthmaECode                            VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaETerm                            VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaEDate                            VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeECode                      VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeETerm                      VARCHAR(200) NULL,  &lt;br /&gt;
  AsthmaEmergeEDate                      VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedECode                    VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedETerm                    VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaResolvedEDate                    VARCHAR(50) NULL,&lt;br /&gt;
  COPDECode                              VARCHAR(50) NULL,&lt;br /&gt;
  COPDETerm                              VARCHAR(200) NULL,&lt;br /&gt;
  COPDEDate                              VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisECode                 VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisETerm                 VARCHAR(200) NULL,&lt;br /&gt;
  PulmonaryFibrosisEDate                 VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseECode           VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseETerm           VARCHAR(200) NULL,&lt;br /&gt;
  InterstitialLungDiseaseEDate           VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationECode    VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationETerm    VARCHAR(200) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationEDate    VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaECode                          VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaETerm                          VARCHAR(200) NULL,&lt;br /&gt;
  GlaucomaEDate                          VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisECode               VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisETerm               VARCHAR(200) NULL,&lt;br /&gt;
  RheumatoidArthritisEDate               VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusECode                     VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusETerm                     VARCHAR(200) NULL,&lt;br /&gt;
  SystemicLupusEDate                     VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseECode          VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseETerm          VARCHAR(200) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseEDate          VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseECode                     VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseETerm                     VARCHAR(200) NULL,&lt;br /&gt;
  CrohnsDiseaseEDate                     VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeECode             VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeETerm             VARCHAR(200) NULL,&lt;br /&gt;
  UlcerativeColitisCodeEDate             VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisECode                  VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisETerm                  VARCHAR(200) NULL,&lt;br /&gt;
  AtopicDermatitisEDate                  VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceECode     VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceETerm     VARCHAR(200) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceEDate     VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaECode          VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaETerm          VARCHAR(200) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaEDate          VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaECode                          VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaETerm                          VARCHAR(200) NULL,&lt;br /&gt;
  MelanomaEDate                          VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerECode                    VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerETerm                    VARCHAR(200) NULL,&lt;br /&gt;
  ProstateCancerEDate                    VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerECode                        VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerETerm                        VARCHAR(200) NULL,&lt;br /&gt;
  LungCancerEDate                        VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerECode                  VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerETerm                  VARCHAR(200) NULL,&lt;br /&gt;
  SmallBowelCancerEDate                  VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerECode                  VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerETerm                  VARCHAR(200) NULL,&lt;br /&gt;
  ColorectalCancerEDate                  VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerECode                      VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerETerm                      VARCHAR(200) NULL,&lt;br /&gt;
  BreastCancerEDate                      VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageECode                       VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageETerm                       VARCHAR(200) NULL,&lt;br /&gt;
  MiscarriageEDate                       VARCHAR(50) NULL&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE diagnoses2dataset ADD INDEX d2_pseudoid_idx (pseudo_id);&lt;br /&gt;
INSERT INTO diagnoses2dataset (pseudo_id) SELECT DISTINCT group_by FROM cohort;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Each column of the dataset is named by combining the observational type, the time frame for the analysis, and the requested field type:&lt;br /&gt;
&lt;br /&gt;
[[File:Naming convention of a dataset column.png]]&lt;br /&gt;
&lt;br /&gt;
The dataset is populated with the pseudo identifiers from the patient cohort, and then SQL update queries populate the dataset table by calling multiple stored procedures.&lt;br /&gt;
&lt;br /&gt;
=== Patient medications dataset ===&lt;br /&gt;
A typical patient medications dataset includes the following information: &lt;br /&gt;
&lt;br /&gt;
[[File:Patient medication dataset.png]]&lt;br /&gt;
=== Create table script for a patient medications dataset table ===&lt;br /&gt;
In the following example the create table script produces the patient medications dataset table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE medications (&lt;br /&gt;
  pseudo_id    VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  nhsnumber    VARCHAR(10)  DEFAULT NULL,&lt;br /&gt;
  codedate     VARCHAR(20)  DEFAULT NULL,&lt;br /&gt;
  codeterm     VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  code         VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codevalue    VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codeunit     VARCHAR(100) DEFAULT NULL&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The dataset is populated with by calling a series of stored procedures.&lt;br /&gt;
&lt;br /&gt;
== Calling stored procedures ==&lt;br /&gt;
Once the datasets and cohorts have been generated, the next step is to populate the datasets using stored procedures.&lt;br /&gt;
&lt;br /&gt;
A series of populate stored procedures call on other stored procedures to complete the update process:&lt;br /&gt;
&lt;br /&gt;
[[File:Stored procedures.png]]&lt;br /&gt;
&lt;br /&gt;
=== Calling the populate stored procedures ===&lt;br /&gt;
In the following example the SQL script calls the populated stored procedures:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL populateCodeDate(0, &#039;CoronaryArteryDiseaseE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;53741008,67682002,398274000,233970002,414545008,63739005&#039;, null, &#039;703356002&#039;, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(1, &#039;CoronaryArteryDiseaseL&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;53741008,67682002,398274000,233970002,414545008,63739005&#039;, null, &#039;703356002&#039;, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;AnginaE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;194828000&#039;, null, null, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;MyocardialInfarctionE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;22298006,399211009&#039;, null, null, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;CoronaryAngioplastyE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;41339005&#039;, null, null, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;CABGE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;232717009,399261000&#039;, null, null, null,&#039;N&#039;);                     &lt;br /&gt;
CALL populateCodeDate(0, &#039;AtrialFibrillationE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;232717009&#039;, null, null, null,&#039;N&#039;);                                &lt;br /&gt;
CALL populateCodeDate(1, &#039;AtrialFibrillationResolvedL&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;196371000000102&#039;, null, null, null,&#039;N&#039;);                           &lt;br /&gt;
CALL populateCodeDate(0, &#039;AtrialFlutterE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;5370000&#039;, null, null, null,&#039;N&#039;);   &lt;br /&gt;
CALL populateCodeDate(0, &#039;HeartFailureE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;84114007,421518007,407596008,407597004,3545003,371037005&#039;, null, null, null,&#039;N&#039;);                                              &lt;br /&gt;
CALL populateCodeDate(0, &#039;TIAE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;266257000,161511000&#039;, null, null, null,&#039;N&#039;);   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The procedure accepts a list of input parameters:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Parameter&lt;br /&gt;
!Data type&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
|-&lt;br /&gt;
|col &lt;br /&gt;
|VARCHAR(100) the root of the column name&lt;br /&gt;
|the root of the column name&lt;br /&gt;
|-&lt;br /&gt;
|datasetTable &lt;br /&gt;
|VARCHAR(100) &lt;br /&gt;
|table name of dataset &lt;br /&gt;
|-&lt;br /&gt;
|reset BIT 1 &lt;br /&gt;
|BIT&lt;br /&gt;
|1 reset, 0 no reset of the dataset table&lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd1&lt;br /&gt;
|VARCHAR(5000) &lt;br /&gt;
|include all snomed parents and their children &lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd2 &lt;br /&gt;
|VARCHAR(5000) &lt;br /&gt;
|include snomed parents only &lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove3 &lt;br /&gt;
|VARCHAR(5000)  &lt;br /&gt;
|exclude snomed parents and their children&lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove4 &lt;br /&gt;
|VARCHAR(5000) &lt;br /&gt;
|exclude only snomed parents &lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls &lt;br /&gt;
|VARCHAR(1) &lt;br /&gt;
|Y or N - whether to ignore null result value&lt;br /&gt;
|}&lt;br /&gt;
A typical patient medications dataset includes the following information: &amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS populateCodeDate;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE populateCodeDate (&lt;br /&gt;
  IN filterType INT, -- 1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
  IN col VARCHAR(100), -- the root of the column name&lt;br /&gt;
  IN datasetTable VARCHAR(100), -- table name of dataset&lt;br /&gt;
  IN reset BIT, -- 1 reset, 0 no reset&lt;br /&gt;
  IN codesToAdd1 VARCHAR(5000), -- all parents and their children&lt;br /&gt;
  IN codesToAdd2 VARCHAR(5000), -- parents only&lt;br /&gt;
  IN codesToRemove3 VARCHAR(5000), -- parents and their children to be excluded&lt;br /&gt;
  IN codesToRemove4 VARCHAR(5000), -- just parents to be excluded&lt;br /&gt;
  IN ignorenulls VARCHAR(1)&lt;br /&gt;
)&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
-- clear out snomeds table&lt;br /&gt;
DELETE FROM snomeds WHERE cat_id IN (1, 2, 3, 4);&lt;br /&gt;
-- clear out store table&lt;br /&gt;
DELETE FROM store WHERE id IN (1, 2, 3, 4);&lt;br /&gt;
&lt;br /&gt;
-- get snomeds&lt;br /&gt;
  IF codesToAdd1 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToAdd1, 1);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (1);&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF codesToAdd2 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToAdd2, 2);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (2);&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF codesToRemove3 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToRemove3, 3);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (3);&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF codesToRemove4 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToRemove4, 4);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (4);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
CALL filterObservations (filterType,1,ignorenulls);&lt;br /&gt;
&lt;br /&gt;
-- reset columns&lt;br /&gt;
IF (reset = 1) THEN&lt;br /&gt;
SET @reset_sql = CONCAT(&#039;UPDATE &#039;, datasetTable, &#039; SET &#039;,&lt;br /&gt;
col, &amp;quot;Code = null, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Term = null, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Date = null&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
PREPARE resetStmt FROM @reset_sql;&lt;br /&gt;
EXECUTE resetStmt;&lt;br /&gt;
DEALLOCATE PREPARE resetStmt;&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
row_id                  INT,&lt;br /&gt;
group_by                VARCHAR(255),&lt;br /&gt;
original_code           VARCHAR(20),&lt;br /&gt;
original_term           VARCHAR(200),&lt;br /&gt;
clinical_effective_date DATE, PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
       f.group_by,&lt;br /&gt;
       f.original_code,&lt;br /&gt;
       f.original_term,&lt;br /&gt;
       f.clinical_effective_date&lt;br /&gt;
FROM filteredObservations f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
&#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Date = date_format(f.clinical_effective_date,&lt;br /&gt;
&#039;%d/%m/%Y&#039;)&lt;br /&gt;
WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
PREPARE stmt FROM @sql;&lt;br /&gt;
EXECUTE stmt;&lt;br /&gt;
DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Retrieving SNOMED codes ===&lt;br /&gt;
The following diagram shows how the SNOMED codes are being retrieved using stored procedures:&lt;br /&gt;
&lt;br /&gt;
[[File:SNOMED stored procedures.png]]&lt;br /&gt;
&lt;br /&gt;
==== Calling the storeSnomedString procedure ====&lt;br /&gt;
The first step of the procedure is to find all the SNOMED codes given a set of parent codes from the input parameter. We pass the codes into the procedure as a comma separated list. &lt;br /&gt;
&lt;br /&gt;
We call the storeSnomedString procedure to separate the list of SNOMED codes into their individual values and store them into a temporary table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS store;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE store (&lt;br /&gt;
  id INT,&lt;br /&gt;
  org_snomed_id BIGINT&lt;br /&gt;
);&lt;br /&gt;
ALTER TABLE store ADD INDEX store_idx (org_snomed_id);&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS storeSnomedString;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE storeSnomedString (&lt;br /&gt;
  IN stringValue VARCHAR(5000),&lt;br /&gt;
  IN cat_id INT&lt;br /&gt;
)&lt;br /&gt;
BEGIN&lt;br /&gt;
    DECLARE front VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
    DECLARE frontlen INT DEFAULT NULL;&lt;br /&gt;
    DECLARE TempValue VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
  processloop:&lt;br /&gt;
  LOOP&lt;br /&gt;
    IF LENGTH(TRIM(stringValue)) = 0 OR stringValue IS NULL THEN&lt;br /&gt;
      LEAVE processloop;&lt;br /&gt;
    END IF;&lt;br /&gt;
  SET front = SUBSTRING_INDEX(stringValue, &#039;,&#039;, 1);&lt;br /&gt;
  SET frontlen = LENGTH(front);&lt;br /&gt;
  SET TempValue = TRIM(front);&lt;br /&gt;
&lt;br /&gt;
  INSERT INTO store (id, org_snomed_id)&lt;br /&gt;
  VALUES (cat_id, CAST(TempValue AS SIGNED));&lt;br /&gt;
&lt;br /&gt;
  SET stringValue = INSERT(stringValue, 1, frontlen + 1, &#039;&#039;);&lt;br /&gt;
&lt;br /&gt;
  END LOOP;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;We repeat this process for each CAT ID flag passed. &lt;br /&gt;
&lt;br /&gt;
==== Calling the getAllSnomedsFromSnomedString procedure ====&lt;br /&gt;
&lt;br /&gt;
We call another procedure getAllSnomedsFromSnomedString to loop through the temporary table, and depending on the CAT ID flag being passed, we retrieve all the corresponding child codes.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS getAllSnomedsFromSnomedString;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE getAllSnomedsFromSnomedString (p_cat_id INT)&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
  DECLARE done INT;&lt;br /&gt;
  DECLARE l_parent_id BIGINT;&lt;br /&gt;
  DECLARE c_get_snomeds CURSOR FOR SELECT org_snomed_id&lt;br /&gt;
                                   FROM store WHERE id = p_cat_id;&lt;br /&gt;
&lt;br /&gt;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;&lt;br /&gt;
&lt;br /&gt;
  SET done = 0;&lt;br /&gt;
&lt;br /&gt;
    OPEN c_get_snomeds;&lt;br /&gt;
    processloop:&lt;br /&gt;
    WHILE (done = 0) DO&lt;br /&gt;
      FETCH c_get_snomeds INTO l_parent_id;&lt;br /&gt;
&lt;br /&gt;
      IF done = 1 THEN&lt;br /&gt;
        LEAVE processloop;&lt;br /&gt;
      END IF;&lt;br /&gt;
&lt;br /&gt;
      IF p_cat_id IN (1, 3) THEN&lt;br /&gt;
        INSERT INTO snomeds (snomed_id, cat_id)&lt;br /&gt;
        SELECT DISTINCT&lt;br /&gt;
               l_parent_id AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id&lt;br /&gt;
        UNION&lt;br /&gt;
        SELECT s.subtypeid AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id&lt;br /&gt;
        FROM sct2_transitiveclosure s&lt;br /&gt;
        WHERE s.supertypeid = l_parent_id&lt;br /&gt;
        AND s.active = 1&lt;br /&gt;
        UNION&lt;br /&gt;
        SELECT s.subtypeid AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id&lt;br /&gt;
        FROM sct2_transitiveclosure s&lt;br /&gt;
        WHERE s.supertypeid IN (SELECT s1.subtypeid&lt;br /&gt;
                                FROM sct2_transitiveclosure s1&lt;br /&gt;
                                WHERE s1.supertypeid = l_parent_id)&lt;br /&gt;
        AND s.active = 1;&lt;br /&gt;
      ELSE&lt;br /&gt;
        INSERT INTO snomeds (snomed_id, cat_id)&lt;br /&gt;
        SELECT l_parent_id AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id;&lt;br /&gt;
      END IF;&lt;br /&gt;
    END WHILE processloop;&lt;br /&gt;
&lt;br /&gt;
    CLOSE c_get_snomeds;&lt;br /&gt;
    SET done = 0;&lt;br /&gt;
&lt;br /&gt;
END //&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The resultant set of codes is stored in a temporary table that is used to query observational data.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS snomeds;&lt;br /&gt;
CREATE TABLE snomeds (&lt;br /&gt;
  cat_id INT,&lt;br /&gt;
  snomed_id BIGINT&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX cat_idx (cat_id);&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX sno_idx (snomed_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The following diagram shows the transitive closure table for looking up the hierarchical information of the SNOMED code: &lt;br /&gt;
&lt;br /&gt;
[[File:Transitive closure table.png]]&lt;br /&gt;
&lt;br /&gt;
Find the corresponding child codes in the subtypeid column by matching the parent code on the supertypeid column.&lt;br /&gt;
&lt;br /&gt;
=== Retrieving filtered Observations ===&lt;br /&gt;
Using the prepared observations cohort, the next step is to filter out this cohort based on the SNOMED codes list and the timeframe, which have been passed in as input parameters from the populate procedure. &lt;br /&gt;
&lt;br /&gt;
The following diagram shows the processing of the FilterObservations procedure:&lt;br /&gt;
&lt;br /&gt;
=== [[File:FilterObservations procedure.png]]  ===&lt;br /&gt;
=== Calling FilterObservations Procedure ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+The following input parameters are passed by calling the FilterObservations procedure:&lt;br /&gt;
!Parameter&lt;br /&gt;
!Data type&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|0 earliest, 1 latest, 2 ever, 3 pivot around cohort pivot date (6 months), 4 all Since&lt;br /&gt;
|-&lt;br /&gt;
|toCreate&lt;br /&gt;
|INT&lt;br /&gt;
|1 is the default value - to call createObservationsFromCohort&lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls&lt;br /&gt;
|VARCHAR(1)&lt;br /&gt;
|Y or N - whether to ignore null result value&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS filterObservations;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE filterObservations (&lt;br /&gt;
    IN filterType INT,&lt;br /&gt;
  -- 0 earliest, 1 latest, 2 ever,&lt;br /&gt;
  -- 3 pivot around cohort pivot date (6 months),&lt;br /&gt;
  -- 4 allSince&lt;br /&gt;
    IN toCreate INT, -- 1 to createObservationsFromCohortv2&lt;br /&gt;
    IN ignorenulls VARCHAR(1) -- Y or N&lt;br /&gt;
 )&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
IF (toCreate = 1) THEN&lt;br /&gt;
CALL createObservationsFromCohort (filterType);&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS filteredObservations;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 0) THEN -- earliest&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
   SELECT&lt;br /&gt;
        ob.id,&lt;br /&gt;
        ob.group_by,&lt;br /&gt;
        ob.patient_id,&lt;br /&gt;
        ob.person_id,&lt;br /&gt;
        ob.original_code,&lt;br /&gt;
        ob.original_term,&lt;br /&gt;
        ob.result_value,&lt;br /&gt;
        ob.clinical_effective_date,&lt;br /&gt;
        ob.result_value_units,&lt;br /&gt;
        ob.age_years,&lt;br /&gt;
        ob.rnk&lt;br /&gt;
    FROM (&lt;br /&gt;
        SELECT o.id,&lt;br /&gt;
               o.group_by,&lt;br /&gt;
               o.patient_id,&lt;br /&gt;
               o.person_id,&lt;br /&gt;
               o.original_code,&lt;br /&gt;
               o.original_term,&lt;br /&gt;
               o.result_value,&lt;br /&gt;
               o.clinical_effective_date,&lt;br /&gt;
               o.result_value_units,&lt;br /&gt;
               o.age_years,&lt;br /&gt;
               @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
               @curperson := o.person_id AS cur_person&lt;br /&gt;
        FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
        WHERE o.result_value IS NOT NULL&lt;br /&gt;
        ORDER BY o.person_id, o.clinical_effective_date ASC, o.id ASC -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
      WHERE ob.rnk = 1;&lt;br /&gt;
  ELSE&lt;br /&gt;
      CREATE TABLE filteredObservations AS&lt;br /&gt;
      SELECT&lt;br /&gt;
        ob.id,&lt;br /&gt;
        ob.group_by,&lt;br /&gt;
        ob.patient_id,&lt;br /&gt;
        ob.person_id,&lt;br /&gt;
        ob.original_code,&lt;br /&gt;
        ob.original_term,&lt;br /&gt;
        ob.result_value,&lt;br /&gt;
        ob.clinical_effective_date,&lt;br /&gt;
        ob.result_value_units,&lt;br /&gt;
        ob.age_years,&lt;br /&gt;
        ob.rnk&lt;br /&gt;
      FROM (&lt;br /&gt;
        SELECT o.id,&lt;br /&gt;
               o.group_by,&lt;br /&gt;
               o.patient_id,&lt;br /&gt;
               o.person_id,&lt;br /&gt;
               o.original_code,&lt;br /&gt;
               o.original_term,&lt;br /&gt;
               o.result_value,&lt;br /&gt;
               o.clinical_effective_date,&lt;br /&gt;
               o.result_value_units,&lt;br /&gt;
               o.age_years,&lt;br /&gt;
               @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
               @curperson := o.person_id AS cur_person&lt;br /&gt;
      FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
      ORDER BY o.person_id, o.clinical_effective_date ASC, o.id ASC -- earliest&lt;br /&gt;
      ) ob&lt;br /&gt;
    WHERE ob.rnk = 1;&lt;br /&gt;
  END IF;&lt;br /&gt;
ELSEIF (filterType = 2 OR filterType = 4) THEN -- ever or allSince&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
     CREATE TABLE filteredObservations AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
         mc.group_by,&lt;br /&gt;
         mc.patient_id,&lt;br /&gt;
         mc.person_id,&lt;br /&gt;
         mc.original_code,&lt;br /&gt;
         mc.original_term,&lt;br /&gt;
         mc.result_value,&lt;br /&gt;
         mc.clinical_effective_date,&lt;br /&gt;
         mc.result_value_units,&lt;br /&gt;
         mc.age_years&lt;br /&gt;
     FROM observationsFromCohort mc&lt;br /&gt;
     WHERE mc.result_value IS NOT NULL;&lt;br /&gt;
  ELSE&lt;br /&gt;
     CREATE TABLE filteredObservations AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
         mc.group_by,&lt;br /&gt;
         mc.patient_id,&lt;br /&gt;
         mc.person_id,&lt;br /&gt;
         mc.original_code,&lt;br /&gt;
         mc.original_term,&lt;br /&gt;
         mc.result_value,&lt;br /&gt;
         mc.clinical_effective_date,&lt;br /&gt;
         mc.result_value_units,&lt;br /&gt;
         mc.age_years&lt;br /&gt;
     FROM observationsFromCohort mc;&lt;br /&gt;
  END IF;&lt;br /&gt;
ELSEIF (filterType = 1 or filterType = 3) THEN -- latest or pivot&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
    CREATE TABLE filteredObservations AS&lt;br /&gt;
    SELECT&lt;br /&gt;
        ob.id,&lt;br /&gt;
        ob.group_by,&lt;br /&gt;
        ob.patient_id,&lt;br /&gt;
        ob.person_id,&lt;br /&gt;
        ob.original_code,&lt;br /&gt;
        ob.original_term,&lt;br /&gt;
        ob.result_value,&lt;br /&gt;
        ob.clinical_effective_date,&lt;br /&gt;
        ob.result_value_units,&lt;br /&gt;
        ob.age_years,&lt;br /&gt;
        ob.rnk&lt;br /&gt;
    FROM (&lt;br /&gt;
    SELECT o.id,&lt;br /&gt;
        o.group_by,&lt;br /&gt;
        o.patient_id,&lt;br /&gt;
        o.person_id,&lt;br /&gt;
        o.original_code,&lt;br /&gt;
        o.original_term,&lt;br /&gt;
        o.result_value,&lt;br /&gt;
        o.clinical_effective_date,&lt;br /&gt;
        o.result_value_units,&lt;br /&gt;
        o.age_years,&lt;br /&gt;
        @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
        @curperson := o.person_id AS cur_person&lt;br /&gt;
    FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
    WHERE o.result_value IS NOT NULL&lt;br /&gt;
    ORDER BY o.person_id, o.clinical_effective_date DESC, o.id DESC -- latest&lt;br /&gt;
      ) ob&lt;br /&gt;
    WHERE ob.rnk = 1;&lt;br /&gt;
  ELSE&lt;br /&gt;
    CREATE TABLE filteredObservations AS&lt;br /&gt;
    SELECT&lt;br /&gt;
      ob.id,&lt;br /&gt;
      ob.group_by,&lt;br /&gt;
      ob.patient_id,&lt;br /&gt;
      ob.person_id,&lt;br /&gt;
      ob.original_code,&lt;br /&gt;
      ob.original_term,&lt;br /&gt;
      ob.result_value,&lt;br /&gt;
      ob.clinical_effective_date,&lt;br /&gt;
      ob.result_value_units,&lt;br /&gt;
      ob.age_years,&lt;br /&gt;
      ob.rnk&lt;br /&gt;
    FROM (&lt;br /&gt;
    SELECT o.id,&lt;br /&gt;
           o.group_by,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           o.original_code,&lt;br /&gt;
           o.original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           o.age_years,&lt;br /&gt;
           @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
           @curperson := o.person_id AS cur_person&lt;br /&gt;
    FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
    ORDER BY o.person_id, o.clinical_effective_date DESC, o.id DESC -- latest&lt;br /&gt;
    ) ob&lt;br /&gt;
    WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
  SIGNAL SQLSTATE &#039;45000&#039;&lt;br /&gt;
  SET MESSAGE_TEXT = &#039;filterType not recognised&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Calling the createobservationsFromCohort procedure ====&lt;br /&gt;
The createobservationsFromCohort procedure is called from the FilterObservations procedure; the filter type is passed as the input parameter:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL createobservationsFromCohort (filterType);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;An example of the createobservationsFromCohort procedure is shown below:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS createObservationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE createObservationsFromCohort (&lt;br /&gt;
  IN filterType INT&lt;br /&gt;
  -- 0 earliest, 1 latest, 2 ever, 3 pivot around cohort_gh2 pivot date (6 months)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS snomeds_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE snomeds_tmp AS&lt;br /&gt;
SELECT cat_id,&lt;br /&gt;
       snomed_id&lt;br /&gt;
FROM snomeds&lt;br /&gt;
WHERE cat_id IN (3, 4);&lt;br /&gt;
&lt;br /&gt;
DELETE t1 FROM snomeds t1 JOIN snomeds_tmp t2&lt;br /&gt;
ON t1.snomed_id = t2.snomed_id&lt;br /&gt;
WHERE t1.cat_id IN (1, 2);&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS observationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 3) THEN&lt;br /&gt;
-- pivot over 6 months from pivot date (already set in cohort)&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE observationsFromCohort AS&lt;br /&gt;
    SELECT DISTINCT&lt;br /&gt;
           o.id,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           o.group_by,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.original_code,&lt;br /&gt;
           SUBSTRING(o.original_term, 1, 200) AS original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           o.age_years&lt;br /&gt;
    FROM cohort_observations o JOIN cohort c&lt;br /&gt;
    ON o.group_by = c.group_by&lt;br /&gt;
    WHERE EXISTS (SELECT &#039;x&#039; FROM snomeds s&lt;br /&gt;
                  WHERE s.cat_id IN (1, 2)&lt;br /&gt;
                  AND s.snomed_id = o.original_code)&lt;br /&gt;
    AND o.clinical_effective_date IS NOT NULL&lt;br /&gt;
    AND o.clinical_effective_date&lt;br /&gt;
    BETWEEN DATE_SUB(c.pivot_date, INTERVAL 6 MONTH)&lt;br /&gt;
    AND DATE_SUB(c.pivot_date, INTERVAL -6 MONTH);&lt;br /&gt;
ELSE&lt;br /&gt;
-- latest or earliest or ever (so all observations)&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE observationsFromCohort AS&lt;br /&gt;
    SELECT DISTINCT&lt;br /&gt;
           o.id,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           o.group_by,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.original_code,&lt;br /&gt;
           SUBSTRING(o.original_term, 1, 200) AS original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           o.age_years&lt;br /&gt;
    FROM cohort_observations o&lt;br /&gt;
    WHERE EXISTS (SELECT &#039;x&#039; FROM snomeds s&lt;br /&gt;
                  WHERE s.cat_id IN (1, 2)&lt;br /&gt;
                  AND s.snomed_id = o.original_code)&lt;br /&gt;
    AND o.clinical_effective_date IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE observationsFromCohort ADD INDEX obv_pat_idx (patient_id);&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;This returns a new observations cohort based on the SNOMED codes list.&lt;br /&gt;
&lt;br /&gt;
==== Filter Observations by Timeframe ====&lt;br /&gt;
&lt;br /&gt;
Using the new observations cohort, we pass in the filter type to retrieve the observations based on the timeframe, and using the clinical effective date to determine the time intervals. We store the result set into a temporary table. This is illustrated in the code snippet of the filterObservations procedure below:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT&lt;br /&gt;
    ob.id,&lt;br /&gt;
    ob.group_by,&lt;br /&gt;
    ob.patient_id,&lt;br /&gt;
    ob.person_id,&lt;br /&gt;
    ob.original_code,&lt;br /&gt;
    ob.original_term,&lt;br /&gt;
    ob.result_value,&lt;br /&gt;
    ob.clinical_effective_date,&lt;br /&gt;
    ob.result_value_units,&lt;br /&gt;
    ob.age_years,&lt;br /&gt;
    ob.rnk&lt;br /&gt;
FROM (&lt;br /&gt;
  SELECT o.id,&lt;br /&gt;
         o.group_by,&lt;br /&gt;
         o.patient_id,&lt;br /&gt;
         o.person_id,&lt;br /&gt;
         o.original_code,&lt;br /&gt;
         o.original_term,&lt;br /&gt;
         o.result_value,&lt;br /&gt;
         o.clinical_effective_date,&lt;br /&gt;
         o.result_value_units,&lt;br /&gt;
         o.age_years,&lt;br /&gt;
         @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
         @curperson := o.person_id AS cur_person&lt;br /&gt;
  FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
  WHERE o.result_value IS NOT NULL&lt;br /&gt;
  ORDER BY o.person_id, o.clinical_effective_date ASC, o.id ASC -- earliest&lt;br /&gt;
  ) ob&lt;br /&gt;
WHERE ob.rnk = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Updating Dataset Table Columns ===&lt;br /&gt;
Using the result set derived from the above step, the dataset table columns are updated with the appropriate observational values. &lt;br /&gt;
&lt;br /&gt;
This is illustrated in the code snippet of the populateCodeDate procedure:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
  row_id                   INT,&lt;br /&gt;
  group_by                 VARCHAR(255),&lt;br /&gt;
  original_code            VARCHAR(20),&lt;br /&gt;
  original_term            VARCHAR(200),&lt;br /&gt;
  clinical_effective_date  DATE, PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
  SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
    f.group_by,&lt;br /&gt;
    f.original_code,&lt;br /&gt;
    f.original_term,&lt;br /&gt;
    f.clinical_effective_date&lt;br /&gt;
  FROM filteredObservations f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
      &#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
      col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
      col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
      col, &amp;quot;Date = date_format(f.clinical_effective_date, &#039;%d/%m/%Y&#039;)&lt;br /&gt;
      WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  PREPARE stmt FROM @sql;&lt;br /&gt;
  EXECUTE stmt;&lt;br /&gt;
  DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>AndreHui</name></author>
	</entry>
	<entry>
		<id>https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_1)&amp;diff=1290</id>
		<title>Remote Subscriber Database (RSD) SQL guide (Compass 1)</title>
		<link rel="alternate" type="text/html" href="https://wiki.voror.co.uk/index.php?title=Remote_Subscriber_Database_(RSD)_SQL_guide_(Compass_1)&amp;diff=1290"/>
		<updated>2020-06-09T11:12:39Z</updated>

		<summary type="html">&lt;p&gt;AndreHui: /* Building the patient cohort SQL with pseudo NHS numbers */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This article describes the steps needed to create, and analyse, patient data selections from the Discovery database.&lt;br /&gt;
&lt;br /&gt;
Datasets are needed to add patient data to a report. Each dataset represents the results gained from running a combination of SQL commands and stored procedures on a data source.&lt;br /&gt;
&lt;br /&gt;
The following diagram shows the algorithm that is followed to generate these datasets:&lt;br /&gt;
&lt;br /&gt;
[[File:Dataset algorithm.jpg|600x600px|The algorithm followed to generate datasets]]&lt;br /&gt;
&lt;br /&gt;
== Patient cohort ==&lt;br /&gt;
&lt;br /&gt;
=== Creating the patient cohort ===&lt;br /&gt;
The first step, when a list of patients marked for observational study is received, is to create a patient cohort. Patient data usually comes into the DDS in the form of pseudonymised personal data. Additional information about the patient can be obtained from the Discovery database using the pseudo identifier or the pseudo NHS identifier.&lt;br /&gt;
&lt;br /&gt;
The following entity diagram shows the relationships between patient and organisation:&lt;br /&gt;
&lt;br /&gt;
[[File:General patient cohort.jpg|500px|General patient cohort]]&lt;br /&gt;
&lt;br /&gt;
Patients are linked to the Discovery database using pseudo patient identifiers. However, if only pseudo NHS numbers are provided, the corresponding pseudo identifiers can be derived from the &#039;&#039;&#039;link distributor&#039;&#039;&#039; table. Patients are filtered by organisation ODS codes to ensure that only the patients of the required GP practices are selected.&lt;br /&gt;
&lt;br /&gt;
=== Building the patient cohort SQL with pseudo NHS numbers ===&lt;br /&gt;
The following example SQL shows how a patient cohort is built using pseudo NHS numbers:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort AS&lt;br /&gt;
SELECT&lt;br /&gt;
       p.id                 AS patient_id,&lt;br /&gt;
       p.pseudo_id          AS group_by,&lt;br /&gt;
       p.person_id          AS person_id,&lt;br /&gt;
       p.organization_id    AS organization_id,&lt;br /&gt;
       n.pseudo_nhsnumber   AS pseudo_nhsnumber,&lt;br /&gt;
       p.age_years&lt;br /&gt;
FROM pseudonhsnumbers n&lt;br /&gt;
JOIN link_distributor l&lt;br /&gt;
  ON n.pseudo_nhsnumber = l.target_skid AND l.target_salt_key_name = &#039;EGH&#039;&lt;br /&gt;
JOIN patient p ON p.pseudo_id = l.source_skid&lt;br /&gt;
JOIN organization org ON p.organization_id = org.id&lt;br /&gt;
WHERE EXISTS (SELECT &#039;x&#039; FROM ccg_codes ccgs&lt;br /&gt;
     WHERE ccgs.parent IN (&#039;City &amp;amp; Hackney CCG&#039;,&#039;Newham CCG&#039;,&#039;Tower Hamlets CCG&#039;,&#039;Waltham Forest CCG&#039;) &lt;br /&gt;
     AND ccgs.local_id = org.ods_code);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Sometimes, the cohort is created with filtered observations.&lt;br /&gt;
&lt;br /&gt;
The example below filters patients who are registered, alive, and have had diabetes. They belong to the age 12 years and over. Two additional tables are introduced for the query: &#039;&#039;&#039;observation&#039;&#039;&#039;(patients who have had diabetes) and &#039;&#039;&#039;episode of care &#039;&#039;&#039;(patients who are registered and have the appropriate registration type for this study).&lt;br /&gt;
&lt;br /&gt;
[[File:Patient observationepisode of care.jpg|500px|Cohorts with filtered observations]]&lt;br /&gt;
&lt;br /&gt;
Patients can be selected with a specific set of observations using a lookup table of relevant&amp;amp;nbsp;SNOMED codes.&amp;lt;br /&amp;gt; Some patients prefer to withhold consent and are therefore are excluded from the cohort;&amp;amp;nbsp;the relevant SNOMED consent code can be checked in the observation table.&amp;lt;br /&amp;gt; Patients are usually only select if they have registration type 2&amp;amp;nbsp;(Regular/GMS).&amp;lt;br /&amp;gt; Sometimes patients are also based on their age in years.&lt;br /&gt;
&lt;br /&gt;
=== Building patient cohort SQL with filtered observations ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort AS&lt;br /&gt;
  SELECT DISTINCT&lt;br /&gt;
         p.id                                 AS patient_id,&lt;br /&gt;
         p.person_id                          AS person_id,&lt;br /&gt;
         p.pseudo_id                          AS group_by,&lt;br /&gt;
         org.id                               AS organization_id,&lt;br /&gt;
         org.ods_code                         AS practice_code,&lt;br /&gt;
         org.name                             AS practice_name,&lt;br /&gt;
         e.date_registered                    AS registered_date,     &lt;br /&gt;
         p.date_of_death                      AS date_of_death&lt;br /&gt;
  FROM observation o&lt;br /&gt;
    JOIN organization org ON org.id = o.organization_id&lt;br /&gt;
    JOIN patient p ON p.id = o.patient_id&lt;br /&gt;
    JOIN episode_of_care e ON e.patient_id = o.patient_id&lt;br /&gt;
  WHERE EXISTS &lt;br /&gt;
  (SELECT &#039;x&#039;FROM snomed_codes s&lt;br /&gt;
   WHERE s.group_id = 1 AND s.snomed_id = o.snomed_concept_id)&lt;br /&gt;
  AND NOT EXISTS &lt;br /&gt;
  (SELECT &#039;x&#039;FROM snomed_codes s&lt;br /&gt;
   WHERE s.group_id = 2 AND s.snomed_id = o.snomed_concept_id)&lt;br /&gt;
  AND org.ods_code IN &lt;br /&gt;
  (SELECT el.local_id FROM elccg_codes el&lt;br /&gt;
   WHERE el.parent IN (&#039;City &amp;amp; Hackney CCG&#039;,&#039;Newham CCG&#039;,&lt;br /&gt;
                       &#039;Tower Hamlets CCG&#039;,&#039;Waltham Forest CCG&#039;))&lt;br /&gt;
  AND p.date_of_death IS NULL&lt;br /&gt;
  AND e.registration_type_id = 2 AND e.date_registered &amp;lt;= now()&lt;br /&gt;
  AND (e.date_registered_end &amp;gt; now() or e.date_registered_end IS NULL) AND (p.age_years &amp;gt;= 12);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Observation cohort ==&lt;br /&gt;
&lt;br /&gt;
=== Creating the observation cohort ===&lt;br /&gt;
&lt;br /&gt;
Observation&amp;amp;nbsp;cohort are created to improve query performance;&amp;amp;nbsp;instead of scanning through the entire observation table, a sub-set of the observation table is created, based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
=== Building the observation cohort SQL&amp;amp;nbsp; ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_observations AS&lt;br /&gt;
	SELECT DISTINCT&lt;br /&gt;
           o.id,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           cr.group_by,&lt;br /&gt;
           cr.pivot_date,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.snomed_concept_id AS original_code,&lt;br /&gt;
           SUBSTRING(o.original_term, 1, 200) AS original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           cr.age_years&lt;br /&gt;
	FROM observation o JOIN cohort cr&lt;br /&gt;
	ON o.person_id = cr.person_id&lt;br /&gt;
	AND o.organization_id = cr.organization_id&lt;br /&gt;
	AND o.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX obs_ix ON cohort_observations(original_code);&lt;br /&gt;
CREATE INDEX obs_grpby_ix ON cohort_observations(group_by);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Medications cohort ==&lt;br /&gt;
&lt;br /&gt;
=== Creating the medications cohort ===&lt;br /&gt;
&lt;br /&gt;
Medications cohort are created to improve query performance;&amp;amp;nbsp;instead of scanning through the entire observation table, a sub-set of the medication statement table is created, based on the patient cohort.&lt;br /&gt;
&lt;br /&gt;
[[File:Patient, medication statement and organization.png|Patient, medication statement and organisation]]&lt;br /&gt;
&lt;br /&gt;
=== Building the medication cohort SQL ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_medications AS&lt;br /&gt;
SELECT DISTINCT&lt;br /&gt;
       m.id,&lt;br /&gt;
       m.dmd_id AS original_code,&lt;br /&gt;
       m.person_id,&lt;br /&gt;
       m.patient_id,&lt;br /&gt;
       cr.group_by,&lt;br /&gt;
       cr.pivot_date,&lt;br /&gt;
       SUBSTRING(m.original_term, 1, 200) AS original_term,&lt;br /&gt;
       m.clinical_effective_date,&lt;br /&gt;
       m.cancellation_date&lt;br /&gt;
FROM medication_statement m JOIN cohort cr&lt;br /&gt;
ON m.organization_id = cr.organization_id&lt;br /&gt;
AND m.person_id = cr.person_id&lt;br /&gt;
AND m.patient_id = cr.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX med_ix ON cohort_medications(original_code);&lt;br /&gt;
CREATE INDEX med_grpby_ix ON cohort_medications(group_by);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other cohorts ==&lt;br /&gt;
&lt;br /&gt;
=== Creating other cohorts ===&lt;br /&gt;
&lt;br /&gt;
This step can apply to any large tables to support improved query performance. For example, the encounter_raw table is a good candidate to create a separate cohort for SQL query; you can query this table if you want information such as the number of face to face encounters, or the number of home visits.&lt;br /&gt;
&lt;br /&gt;
[[File:Encounter.png|Encounter raw entity]]&lt;br /&gt;
&lt;br /&gt;
=== Building the encounter raw cohort SQL ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE cohort_encounter_raw AS&lt;br /&gt;
SELECT r.person_id,&lt;br /&gt;
       r.patient_id,&lt;br /&gt;
       r.clinical_effective_date,&lt;br /&gt;
       SUBSTRING(UPPER(r.fhir_original_term),1,200) fhir_original_term,&lt;br /&gt;
       r.organization_id&lt;br /&gt;
FROM encounter_raw r&lt;br /&gt;
JOIN cohort c ON r.patient_id = c.patient_id;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX encounter_term_idx ON cohort_encounter_raw(fhir_original_term);&lt;br /&gt;
CREATE INDEX encounter_person_idx ON cohort_encounter_raw(person_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building datasets ==&lt;br /&gt;
This involves creating a number of tables in the database.&lt;br /&gt;
&lt;br /&gt;
=== Patient demographic dataset ===&lt;br /&gt;
A typical patient demographic dataset includes the following information:&lt;br /&gt;
&lt;br /&gt;
[[File:Patient demographic dataset.png|Patient demographic dataset]] &lt;br /&gt;
&lt;br /&gt;
This information is translated into a &#039;create table&#039; script, which is then run to create the dataset table in the database.&lt;br /&gt;
&lt;br /&gt;
=== Create table script for a patient demographic dataset table ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE demographicsDataset (&lt;br /&gt;
	ExtractDate           DATETIME NULL,&lt;br /&gt;
	Pseudo_id             VARCHAR(255) NULL,&lt;br /&gt;
	Pseudo_NHSNumber      VARCHAR(255) NULL,&lt;br /&gt;
	Gender                VARCHAR(50) NULL,&lt;br /&gt;
	Age                   VARCHAR(50) NULL,&lt;br /&gt;
	DateOfBirth           DATE NULL,&lt;br /&gt;
	EthnicityLCode        VARCHAR(50) NULL,&lt;br /&gt;
	EthnicityLTerm        VARCHAR(200) NULL,&lt;br /&gt;
	BirthCountryLCode     VARCHAR(50) NULL,&lt;br /&gt;
	BirthCountryLTerm     VARCHAR(200) NULL,&lt;br /&gt;
	RegistrationStart     DATE NULL,&lt;br /&gt;
	RegistrationEnd       DATE NULL,&lt;br /&gt;
	IMD2010               VARCHAR(50) NULL,&lt;br /&gt;
	LSOA2011              VARCHAR(50) NULL,&lt;br /&gt;
	PracticeODSCode       VARCHAR(50) NULL,&lt;br /&gt;
	PracticeODSName       VARCHAR(255) NULL,&lt;br /&gt;
	CCGName               VARCHAR(100) NULL,&lt;br /&gt;
	YearOfDeath           INT(4) NULL,&lt;br /&gt;
	F2fVisits_Total       INT(11) DEFAULT 0,&lt;br /&gt;
	F2fVisits_1year       INT(11) DEFAULT 0,&lt;br /&gt;
	F2fVisits_5years      INT(11) DEFAULT 0&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE demographicsDataset ADD INDEX demo_pseudoid_idx (pseudo_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The dataset is then populated  with pseudo identifiers from the patient cohort.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
INSERT INTO demographicsDataset (pseudo_id, extractdate) &lt;br /&gt;
SELECT DISTINCT group_by, now() FROM cohort;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Separate update SQL queries then populate the columns of the dataset table. For example:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_age;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_age (&lt;br /&gt;
	row_id INT,&lt;br /&gt;
	group_by VARCHAR(255),&lt;br /&gt;
	age_years INT,&lt;br /&gt;
	lsoa_code VARCHAR(50), PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
	c.group_by,&lt;br /&gt;
	p.age_years,&lt;br /&gt;
	p.lsoa_code&lt;br /&gt;
FROM patient p JOIN cohort c ON p.pseudo_id = c.group_by, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS&lt;br /&gt;
(SELECT row_id from qry_age WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN qry_age q ON d.pseudo_id = q.group_by&lt;br /&gt;
	SET d.Age = q.age_years,&lt;br /&gt;
	d.LSOA2011 = q.lsoa_code&lt;br /&gt;
	WHERE q.row_id &amp;gt; @row_id AND q.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In the following example, the age and LSOA code are derived from the Patient table. The demographic dataset table is updated with those values when there is a match on the pseudo identifier.&lt;br /&gt;
&lt;br /&gt;
If the update is large, it might be possible to optimise the update process by using batches; for example to add IMD2010:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_imd;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_imd (&lt;br /&gt;
	row_id INT,&lt;br /&gt;
	pseudo_id VARCHAR(255),&lt;br /&gt;
	IMD2010 DECIMAL(5,3), PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
	d.pseudo_id,&lt;br /&gt;
	lso.imd_score&lt;br /&gt;
FROM demographicsDataset d JOIN lsoa_lookup lso ON d.LSOA2011 = lso.lsoa_code, (SELECT @row_no := 0) t&lt;br /&gt;
WHERE d.lsoa2011 IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS&lt;br /&gt;
(SELECT row_id from qry_imd WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN qry_imd i ON d.pseudo_id = i.pseudo_id&lt;br /&gt;
	SET d.IMD2010 = i.imd_score&lt;br /&gt;
	WHERE i.row_id &amp;gt; @row_id AND i.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Add registration start date, registration end date, practice ODS code, practice ODS name:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS reg_sort;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_reg;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_reg AS&lt;br /&gt;
SELECT 	c.group_by,&lt;br /&gt;
		c.person_id,&lt;br /&gt;
		e.date_registered,&lt;br /&gt;
		e.date_registered_end,&lt;br /&gt;
		o.name,&lt;br /&gt;
		o.ods_code,&lt;br /&gt;
		g.parent&lt;br /&gt;
FROM cohort c&lt;br /&gt;
JOIN episode_of_care e ON e.person_id = c.person_id&lt;br /&gt;
JOIN organization o ON o.id = e.organization_id&lt;br /&gt;
JOIN ccg_codes g ON g.local_id = o.ods_code;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE reg_sort (&lt;br /&gt;
	row_id                  INT,&lt;br /&gt;
	group_by                VARCHAR(255),&lt;br /&gt;
	person_id               BIGINT,&lt;br /&gt;
	date_registered         DATE,&lt;br /&gt;
	date_regiostered_end    DATE,&lt;br /&gt;
	ode_code                VARCHAR(50),&lt;br /&gt;
	name                    VARCHAR(255),&lt;br /&gt;
	parent                  VARCHAR(100),&lt;br /&gt;
	rnk                     INT, PRIMARY KEY(row_id)) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
	a.group_by,&lt;br /&gt;
	a.person_id,&lt;br /&gt;
	a.date_registered,&lt;br /&gt;
	a.date_registered_end,&lt;br /&gt;
	a.ods_code,&lt;br /&gt;
	a.name,&lt;br /&gt;
	a.parent,&lt;br /&gt;
	a.rnk&lt;br /&gt;
FROM (SELECT  q.group_by,&lt;br /&gt;
              q.person_id,&lt;br /&gt;
              q.date_registered,&lt;br /&gt;
              q.date_registered_end,&lt;br /&gt;
              q.name,&lt;br /&gt;
              q.ods_code,&lt;br /&gt;
              q.parent,&lt;br /&gt;
              @currank := IF(@curperson = q.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
              @curperson := q.person_id AS cur_person&lt;br /&gt;
      FROM qry_reg q, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
      ORDER BY q.person_id, q.date_registered DESC ) a, (SELECT @row_no := 0) t&lt;br /&gt;
WHERE a.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from reg_sort&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN reg_sort reg ON d.pseudo_id = reg.group_by&lt;br /&gt;
	SET d.RegistrationStart = reg.date_registered,&lt;br /&gt;
		d.RegistrationEnd = reg.date_registered_end,&lt;br /&gt;
		d.PracticeODSCode = reg.ods_code,&lt;br /&gt;
		d.PracticeODSName = reg.name,&lt;br /&gt;
		d.CCGName = reg.parent&lt;br /&gt;
	WHERE reg.row_id &amp;gt; @row_id&lt;br /&gt;
	AND reg.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In the above example, two temporary tables are created; the first to hold the registration data, and the second to select patient records that have the latest registration date.&lt;br /&gt;
&lt;br /&gt;
Add the total number of face to face visits:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS noVisitsTotal;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE noVisitsTotal (&lt;br /&gt;
	row_id INT,&lt;br /&gt;
	group_by VARCHAR(255),&lt;br /&gt;
	visits INT, PRIMARY KEY(row_id) ) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
       b.group_by,&lt;br /&gt;
       b.visits&lt;br /&gt;
FROM (SELECT cr.group_by AS group_by,&lt;br /&gt;
             COUNT(DISTINCT e.clinical_effective_date) AS visits&lt;br /&gt;
      FROM cohort_encounter_raw e JOIN cohort cr&lt;br /&gt;
      ON e.person_id = cr.person_id&lt;br /&gt;
      WHERE EXISTS (SELECT &#039;x&#039; FROM f2fEncounters s&lt;br /&gt;
                    WHERE s.term = e.fhir_original_term)&lt;br /&gt;
                    AND e.clinical_effective_date IS NOT NULL&lt;br /&gt;
                    AND e.fhir_original_term IS NOT NULL&lt;br /&gt;
                    GROUP BY cr.group_by) b, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from noVisitsTotal&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
	UPDATE demographicsDataset d&lt;br /&gt;
	JOIN noVisitsTotal nvt ON d.pseudo_id = nvt.group_by&lt;br /&gt;
	SET d.F2fVisits_Total = nvt.visits&lt;br /&gt;
	WHERE nvt.row_id &amp;gt; @row_id AND nvt.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
	SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Face to face visits are identified by the FHIR original terms in the Encounter table. The count distinct is used on the clinical effective date to identify the number of distinct visits.&lt;br /&gt;
&lt;br /&gt;
For example, add the total number of face to face visits in the last 1 year:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS lastyearvisits;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE lastyearvisits (&lt;br /&gt;
  row_id INT,&lt;br /&gt;
  group_by VARCHAR(255),&lt;br /&gt;
  visits INT, PRIMARY KEY(row_id) ) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
       b.group_by,&lt;br /&gt;
       b.visits&lt;br /&gt;
FROM (SELECT cr.group_by AS group_by,&lt;br /&gt;
             COUNT(DISTINCT e.clinical_effective_date) AS visits&lt;br /&gt;
      FROM cohort_encounter_raw e JOIN cohort cr&lt;br /&gt;
      ON e.person_id = cr.person_id&lt;br /&gt;
      WHERE EXISTS (SELECT &#039;x&#039; FROM f2fEncounters s&lt;br /&gt;
                    WHERE s.term = e.fhir_original_term)&lt;br /&gt;
      AND e.clinical_effective_date &amp;gt; DATE_SUB(now(), INTERVAL 12 MONTH) -- last 1 yr&lt;br /&gt;
      AND e.clinical_effective_date IS NOT NULL&lt;br /&gt;
      AND e.fhir_original_term IS NOT NULL&lt;br /&gt;
      GROUP BY cr.group_by) b, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from lastyearvisits&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  UPDATE demographicsDataset d&lt;br /&gt;
  JOIN lastyearvisits lyr ON d.pseudo_id = lyr.group_by&lt;br /&gt;
  SET d.F2fVisits_1year = lyr.visits&lt;br /&gt;
  WHERE lyr.row_id &amp;gt; @row_id AND lyr.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The clinical effective date is used to determine the time interval.&amp;lt;br /&amp;gt;For example, add the total number of face to face visits for the last 5 years:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS lastfiveyearvisits;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE lastfiveyearvisits (&lt;br /&gt;
  row_id INT,&lt;br /&gt;
  group_by VARCHAR(255),&lt;br /&gt;
  visits INT, PRIMARY KEY(row_id) ) AS&lt;br /&gt;
SELECT (@row_no := @row_no + 1) AS row_id,&lt;br /&gt;
       b.group_by,&lt;br /&gt;
       b.visits&lt;br /&gt;
FROM (SELECT cr.group_by AS group_by,&lt;br /&gt;
             COUNT(DISTINCT e.clinical_effective_date) AS visits&lt;br /&gt;
      FROM cohort_encounter_raw e JOIN cohort cr &lt;br /&gt;
      ON e.person_id = cr.person_id&lt;br /&gt;
      WHERE EXISTS (SELECT &#039;x&#039; FROM f2fEncounters s&lt;br /&gt;
                    WHERE s.term = e.fhir_original_term)&lt;br /&gt;
      AND e.clinical_effective_date &amp;gt; DATE_SUB(now(), INTERVAL 60 MONTH) -- last 5 yrs&lt;br /&gt;
      AND e.clinical_effective_date IS NOT NULL&lt;br /&gt;
      AND e.fhir_original_term IS NOT NULL&lt;br /&gt;
      GROUP BY cr.group_by) b, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from lastfiveyearvisits&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  UPDATE demographicsDataset d&lt;br /&gt;
  JOIN lastfiveyearvisits lfyr ON d.pseudo_id = lfyr.group_by&lt;br /&gt;
  SET d.F2fVisits_5years = lfyr.visits&lt;br /&gt;
  WHERE lfyr.row_id &amp;gt; @row_id AND lfyr.row_id &amp;lt;= @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;Note:&#039;&#039;&#039; the patient&#039;s name, age, and date of birth are embedded in the pseudo identifier string that needs to be de-anonymised before it is sent to the recipient.&lt;br /&gt;
&lt;br /&gt;
=== Patient observations dataset ===&lt;br /&gt;
A typical patient observations dataset includes the following information: &lt;br /&gt;
&lt;br /&gt;
[[File:Patient Observations Dataset.png]]&lt;br /&gt;
&lt;br /&gt;
A typical dataset can contain 100 to 200 fields, and usually there are several datasets to deliver.&lt;br /&gt;
&lt;br /&gt;
=== Create table script for a patient diagnosis dataset table ===&lt;br /&gt;
In the following example the create table script produces the patient diagnoses dataset table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS diagnoses2dataset;&lt;br /&gt;
CREATE TABLE diagnoses2dataset (&lt;br /&gt;
  Pseudo_id                     VARCHAR(255) NULL,&lt;br /&gt;
  Pseudo_NHSNumber              VARCHAR(255) NULL,&lt;br /&gt;
  AsthmaECode                   VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaETerm                   VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaEDate                   VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeECode             VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaEmergeETerm             VARCHAR(200) NULL,  &lt;br /&gt;
  AsthmaEmergeEDate             VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedECode           VARCHAR(50) NULL,&lt;br /&gt;
  AsthmaResolvedETerm           VARCHAR(200) NULL,&lt;br /&gt;
  AsthmaResolvedEDate           VARCHAR(50) NULL,&lt;br /&gt;
  COPDECode                     VARCHAR(50) NULL,&lt;br /&gt;
  COPDETerm                     VARCHAR(200) NULL,&lt;br /&gt;
  COPDEDate                     VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisECode        VARCHAR(50) NULL,&lt;br /&gt;
  PulmonaryFibrosisETerm        VARCHAR(200) NULL,&lt;br /&gt;
  PulmonaryFibrosisEDate        VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseECode  VARCHAR(50) NULL,&lt;br /&gt;
  InterstitialLungDiseaseETerm  VARCHAR(200) NULL,&lt;br /&gt;
  InterstitialLungDiseaseEDate  VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationECode    VARCHAR(50) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationETerm    VARCHAR(200) NULL,&lt;br /&gt;
  AgeRelatedMuscularDegenerationEDate    VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaECode                 VARCHAR(50) NULL,&lt;br /&gt;
  GlaucomaETerm                 VARCHAR(200) NULL,&lt;br /&gt;
  GlaucomaEDate                 VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisECode      VARCHAR(50) NULL,&lt;br /&gt;
  RheumatoidArthritisETerm      VARCHAR(200) NULL,&lt;br /&gt;
  RheumatoidArthritisEDate      VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusECode            VARCHAR(50) NULL,&lt;br /&gt;
  SystemicLupusETerm            VARCHAR(200) NULL,&lt;br /&gt;
  SystemicLupusEDate            VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseECode VARCHAR(50) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseETerm VARCHAR(200) NULL,&lt;br /&gt;
  InflammatoryBowelDiseaseEDate VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseECode            VARCHAR(50) NULL,&lt;br /&gt;
  CrohnsDiseaseETerm            VARCHAR(200) NULL,&lt;br /&gt;
  CrohnsDiseaseEDate            VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeECode    VARCHAR(50) NULL,&lt;br /&gt;
  UlcerativeColitisCodeETerm    VARCHAR(200) NULL,&lt;br /&gt;
  UlcerativeColitisCodeEDate    VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisECode         VARCHAR(50) NULL,&lt;br /&gt;
  AtopicDermatitisETerm         VARCHAR(200) NULL,&lt;br /&gt;
  AtopicDermatitisEDate         VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceECode     VARCHAR(50) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceETerm     VARCHAR(200) NULL,&lt;br /&gt;
  InheritedMucociliaryClearanceEDate     VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaECode          VARCHAR(50) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaETerm          VARCHAR(200) NULL,&lt;br /&gt;
  PrimaryCiliaryDyskinesiaEDate          VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaECode                  VARCHAR(50) NULL,&lt;br /&gt;
  MelanomaETerm                  VARCHAR(200) NULL,&lt;br /&gt;
  MelanomaEDate                  VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerECode            VARCHAR(50) NULL,&lt;br /&gt;
  ProstateCancerETerm            VARCHAR(200) NULL,&lt;br /&gt;
  ProstateCancerEDate            VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerECode                VARCHAR(50) NULL,&lt;br /&gt;
  LungCancerETerm                VARCHAR(200) NULL,&lt;br /&gt;
  LungCancerEDate                VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerECode          VARCHAR(50) NULL,&lt;br /&gt;
  SmallBowelCancerETerm          VARCHAR(200) NULL,&lt;br /&gt;
  SmallBowelCancerEDate          VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerECode          VARCHAR(50) NULL,&lt;br /&gt;
  ColorectalCancerETerm          VARCHAR(200) NULL,&lt;br /&gt;
  ColorectalCancerEDate          VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerECode              VARCHAR(50) NULL,&lt;br /&gt;
  BreastCancerETerm              VARCHAR(200) NULL,&lt;br /&gt;
  BreastCancerEDate              VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageECode               VARCHAR(50) NULL,&lt;br /&gt;
  MiscarriageETerm               VARCHAR(200) NULL,&lt;br /&gt;
  MiscarriageEDate               VARCHAR(50) NULL&lt;br /&gt;
);&lt;br /&gt;
ALTER TABLE diagnoses2dataset ADD INDEX d2_pseudoid_idx (pseudo_id);&lt;br /&gt;
INSERT INTO diagnoses2dataset (pseudo_id) SELECT DISTINCT group_by FROM cohort;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Each column of the dataset is named by combining the observational type, the time frame for the analysis, and the requested field type:&lt;br /&gt;
&lt;br /&gt;
[[File:Naming convention of a dataset column.png]]&lt;br /&gt;
&lt;br /&gt;
The dataset is populated with the pseudo identifiers from the patient cohort, and then SQL update queries populate the dataset table by calling multiple stored procedures.&lt;br /&gt;
&lt;br /&gt;
=== Patient medications dataset ===&lt;br /&gt;
A typical patient medications dataset includes the following information: &lt;br /&gt;
&lt;br /&gt;
[[File:Patient medication dataset.png]]&lt;br /&gt;
=== Create table script for a patient medications dataset table ===&lt;br /&gt;
In the following example the create table script produces the patient medications dataset table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE medications (&lt;br /&gt;
  pseudo_id    VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  nhsnumber    VARCHAR(10)  DEFAULT NULL,&lt;br /&gt;
  codedate     VARCHAR(20)  DEFAULT NULL,&lt;br /&gt;
  codeterm     VARCHAR(255) DEFAULT NULL,&lt;br /&gt;
  code         VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codevalue    VARCHAR(100) DEFAULT NULL,&lt;br /&gt;
  codeunit     VARCHAR(100) DEFAULT NULL&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The dataset is populated with by calling a series of stored procedures.&lt;br /&gt;
&lt;br /&gt;
== Calling stored procedures ==&lt;br /&gt;
Once the datasets and cohorts have been generated, the next step is to populate the datasets using stored procedures.&lt;br /&gt;
&lt;br /&gt;
A series of populate stored procedures call on other stored procedures to complete the update process:&lt;br /&gt;
&lt;br /&gt;
[[File:Stored procedures.png]]&lt;br /&gt;
&lt;br /&gt;
=== Calling the populate stored procedures ===&lt;br /&gt;
In the following example the SQL script calls the populated stored procedures:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL populateCodeDate(0, &#039;CoronaryArteryDiseaseE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;53741008,67682002,398274000,233970002,414545008,63739005&#039;, null, &#039;703356002&#039;, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(1, &#039;CoronaryArteryDiseaseL&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;53741008,67682002,398274000,233970002,414545008,63739005&#039;, null, &#039;703356002&#039;, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;AnginaE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;194828000&#039;, null, null, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;MyocardialInfarctionE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;22298006,399211009&#039;, null, null, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;CoronaryAngioplastyE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;41339005&#039;, null, null, null,&#039;N&#039;);&lt;br /&gt;
CALL populateCodeDate(0, &#039;CABGE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;232717009,399261000&#039;, null, null, null,&#039;N&#039;);                     &lt;br /&gt;
CALL populateCodeDate(0, &#039;AtrialFibrillationE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;232717009&#039;, null, null, null,&#039;N&#039;);                                &lt;br /&gt;
CALL populateCodeDate(1, &#039;AtrialFibrillationResolvedL&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;196371000000102&#039;, null, null, null,&#039;N&#039;);                           &lt;br /&gt;
CALL populateCodeDate(0, &#039;AtrialFlutterE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;5370000&#039;, null, null, null,&#039;N&#039;);   &lt;br /&gt;
CALL populateCodeDate(0, &#039;HeartFailureE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;84114007,421518007,407596008,407597004,3545003,371037005&#039;, null, null, null,&#039;N&#039;);                                              &lt;br /&gt;
CALL populateCodeDate(0, &#039;TIAE&#039;, &#039;diagnoses1adataset&#039;, 0, &#039;266257000,161511000&#039;, null, null, null,&#039;N&#039;);   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The procedure accepts a list of input parameters:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Parameter&lt;br /&gt;
!Data type&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
|-&lt;br /&gt;
|col &lt;br /&gt;
|VARCHAR(100) the root of the column name&lt;br /&gt;
|the root of the column name&lt;br /&gt;
|-&lt;br /&gt;
|datasetTable &lt;br /&gt;
|VARCHAR(100) &lt;br /&gt;
|table name of dataset &lt;br /&gt;
|-&lt;br /&gt;
|reset BIT 1 &lt;br /&gt;
|BIT&lt;br /&gt;
|1 reset, 0 no reset of the dataset table&lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd1&lt;br /&gt;
|VARCHAR(5000) &lt;br /&gt;
|include all snomed parents and their children &lt;br /&gt;
|-&lt;br /&gt;
|codesToAdd2 &lt;br /&gt;
|VARCHAR(5000) &lt;br /&gt;
|include snomed parents only &lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove3 &lt;br /&gt;
|VARCHAR(5000)  &lt;br /&gt;
|exclude snomed parents and their children&lt;br /&gt;
|-&lt;br /&gt;
|codesToRemove4 &lt;br /&gt;
|VARCHAR(5000) &lt;br /&gt;
|exclude only snomed parents &lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls &lt;br /&gt;
|VARCHAR(1) &lt;br /&gt;
|Y or N - whether to ignore null result value&lt;br /&gt;
|}&lt;br /&gt;
A typical patient medications dataset includes the following information: &amp;lt;br /&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS populateCodeDate;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE populateCodeDate (&lt;br /&gt;
  IN filterType INT, -- 1 latest, 0 earliest, 2 ever, 3 pivot&lt;br /&gt;
  IN col VARCHAR(100), -- the root of the column name&lt;br /&gt;
  IN datasetTable VARCHAR(100), -- table name of dataset&lt;br /&gt;
  IN reset BIT, -- 1 reset, 0 no reset&lt;br /&gt;
  IN codesToAdd1 VARCHAR(5000), -- all parents and their children&lt;br /&gt;
  IN codesToAdd2 VARCHAR(5000), -- parents only&lt;br /&gt;
  IN codesToRemove3 VARCHAR(5000), -- parents and their children to be excluded&lt;br /&gt;
  IN codesToRemove4 VARCHAR(5000), -- just parents to be excluded&lt;br /&gt;
  IN ignorenulls VARCHAR(1)&lt;br /&gt;
)&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
-- clear out snomeds table&lt;br /&gt;
DELETE FROM snomeds WHERE cat_id IN (1, 2, 3, 4);&lt;br /&gt;
-- clear out store table&lt;br /&gt;
DELETE FROM store WHERE id IN (1, 2, 3, 4);&lt;br /&gt;
&lt;br /&gt;
-- get snomeds&lt;br /&gt;
  IF codesToAdd1 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToAdd1, 1);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (1);&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF codesToAdd2 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToAdd2, 2);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (2);&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF codesToRemove3 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToRemove3, 3);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (3);&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF codesToRemove4 IS NOT NULL THEN&lt;br /&gt;
    CALL storeSnomedString (codesToRemove4, 4);&lt;br /&gt;
    CALL getAllSnomedsFromSnomedString (4);&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
CALL filterObservations (filterType,1,ignorenulls);&lt;br /&gt;
&lt;br /&gt;
-- reset columns&lt;br /&gt;
IF (reset = 1) THEN&lt;br /&gt;
SET @reset_sql = CONCAT(&#039;UPDATE &#039;, datasetTable, &#039; SET &#039;,&lt;br /&gt;
col, &amp;quot;Code = null, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Term = null, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Date = null&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
PREPARE resetStmt FROM @reset_sql;&lt;br /&gt;
EXECUTE resetStmt;&lt;br /&gt;
DEALLOCATE PREPARE resetStmt;&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
row_id                  INT,&lt;br /&gt;
group_by                VARCHAR(255),&lt;br /&gt;
original_code           VARCHAR(20),&lt;br /&gt;
original_term           VARCHAR(200),&lt;br /&gt;
clinical_effective_date DATE, PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
       f.group_by,&lt;br /&gt;
       f.original_code,&lt;br /&gt;
       f.original_term,&lt;br /&gt;
       f.clinical_effective_date&lt;br /&gt;
FROM filteredObservations f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
&#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
col, &amp;quot;Date = date_format(f.clinical_effective_date,&lt;br /&gt;
&#039;%d/%m/%Y&#039;)&lt;br /&gt;
WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
PREPARE stmt FROM @sql;&lt;br /&gt;
EXECUTE stmt;&lt;br /&gt;
DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Retrieving SNOMED codes ===&lt;br /&gt;
The following diagram shows how the SNOMED codes are being retrieved using stored procedures:&lt;br /&gt;
&lt;br /&gt;
[[File:SNOMED stored procedures.png]]&lt;br /&gt;
&lt;br /&gt;
==== Calling the storeSnomedString procedure ====&lt;br /&gt;
The first step of the procedure is to find all the SNOMED codes given a set of parent codes from the input parameter. We pass the codes into the procedure as a comma separated list. &lt;br /&gt;
&lt;br /&gt;
We call the storeSnomedString procedure to separate the list of SNOMED codes into their individual values and store them into a temporary table:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS store;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE store (&lt;br /&gt;
  id INT,&lt;br /&gt;
  org_snomed_id BIGINT&lt;br /&gt;
);&lt;br /&gt;
ALTER TABLE store ADD INDEX store_idx (org_snomed_id);&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS storeSnomedString;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE storeSnomedString (&lt;br /&gt;
  IN stringValue VARCHAR(5000),&lt;br /&gt;
  IN cat_id INT&lt;br /&gt;
)&lt;br /&gt;
BEGIN&lt;br /&gt;
    DECLARE front VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
    DECLARE frontlen INT DEFAULT NULL;&lt;br /&gt;
    DECLARE TempValue VARCHAR(5000) DEFAULT NULL;&lt;br /&gt;
  processloop:&lt;br /&gt;
  LOOP&lt;br /&gt;
    IF LENGTH(TRIM(stringValue)) = 0 OR stringValue IS NULL THEN&lt;br /&gt;
      LEAVE processloop;&lt;br /&gt;
    END IF;&lt;br /&gt;
  SET front = SUBSTRING_INDEX(stringValue, &#039;,&#039;, 1);&lt;br /&gt;
  SET frontlen = LENGTH(front);&lt;br /&gt;
  SET TempValue = TRIM(front);&lt;br /&gt;
&lt;br /&gt;
  INSERT INTO store (id, org_snomed_id)&lt;br /&gt;
  VALUES (cat_id, CAST(TempValue AS SIGNED));&lt;br /&gt;
&lt;br /&gt;
  SET stringValue = INSERT(stringValue, 1, frontlen + 1, &#039;&#039;);&lt;br /&gt;
&lt;br /&gt;
  END LOOP;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;We repeat this process for each CAT ID flag passed. &lt;br /&gt;
&lt;br /&gt;
==== Calling the getAllSnomedsFromSnomedString procedure ====&lt;br /&gt;
&lt;br /&gt;
We call another procedure getAllSnomedsFromSnomedString to loop through the temporary table, and depending on the CAT ID flag being passed, we retrieve all the corresponding child codes.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS getAllSnomedsFromSnomedString;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE getAllSnomedsFromSnomedString (p_cat_id INT)&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
  DECLARE done INT;&lt;br /&gt;
  DECLARE l_parent_id BIGINT;&lt;br /&gt;
  DECLARE c_get_snomeds CURSOR FOR SELECT org_snomed_id&lt;br /&gt;
                                   FROM store WHERE id = p_cat_id;&lt;br /&gt;
&lt;br /&gt;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;&lt;br /&gt;
&lt;br /&gt;
  SET done = 0;&lt;br /&gt;
&lt;br /&gt;
    OPEN c_get_snomeds;&lt;br /&gt;
    processloop:&lt;br /&gt;
    WHILE (done = 0) DO&lt;br /&gt;
      FETCH c_get_snomeds INTO l_parent_id;&lt;br /&gt;
&lt;br /&gt;
      IF done = 1 THEN&lt;br /&gt;
        LEAVE processloop;&lt;br /&gt;
      END IF;&lt;br /&gt;
&lt;br /&gt;
      IF p_cat_id IN (1, 3) THEN&lt;br /&gt;
        INSERT INTO snomeds (snomed_id, cat_id)&lt;br /&gt;
        SELECT DISTINCT&lt;br /&gt;
               l_parent_id AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id&lt;br /&gt;
        UNION&lt;br /&gt;
        SELECT s.subtypeid AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id&lt;br /&gt;
        FROM sct2_transitiveclosure s&lt;br /&gt;
        WHERE s.supertypeid = l_parent_id&lt;br /&gt;
        AND s.active = 1&lt;br /&gt;
        UNION&lt;br /&gt;
        SELECT s.subtypeid AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id&lt;br /&gt;
        FROM sct2_transitiveclosure s&lt;br /&gt;
        WHERE s.supertypeid IN (SELECT s1.subtypeid&lt;br /&gt;
                                FROM sct2_transitiveclosure s1&lt;br /&gt;
                                WHERE s1.supertypeid = l_parent_id)&lt;br /&gt;
        AND s.active = 1;&lt;br /&gt;
      ELSE&lt;br /&gt;
        INSERT INTO snomeds (snomed_id, cat_id)&lt;br /&gt;
        SELECT l_parent_id AS snomed_id,&lt;br /&gt;
               p_cat_id AS cat_id;&lt;br /&gt;
      END IF;&lt;br /&gt;
    END WHILE processloop;&lt;br /&gt;
&lt;br /&gt;
    CLOSE c_get_snomeds;&lt;br /&gt;
    SET done = 0;&lt;br /&gt;
&lt;br /&gt;
END //&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The resultant set of codes is stored in a temporary table that is used to query observational data.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS snomeds;&lt;br /&gt;
CREATE TABLE snomeds (&lt;br /&gt;
  cat_id INT,&lt;br /&gt;
  snomed_id BIGINT&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX cat_idx (cat_id);&lt;br /&gt;
ALTER TABLE snomeds ADD INDEX sno_idx (snomed_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The following diagram shows the transitive closure table for looking up the hierarchical information of the SNOMED code: &lt;br /&gt;
&lt;br /&gt;
[[File:Transitive closure table.png]]&lt;br /&gt;
&lt;br /&gt;
Find the corresponding child codes in the subtypeid column by matching the parent code on the supertypeid column.&lt;br /&gt;
&lt;br /&gt;
=== Retrieving filtered Observations ===&lt;br /&gt;
Using the prepared observations cohort, the next step is to filter out this cohort based on the SNOMED codes list and the timeframe, which have been passed in as input parameters from the populate procedure. &lt;br /&gt;
&lt;br /&gt;
The following diagram shows the processing of the FilterObservations procedure:&lt;br /&gt;
&lt;br /&gt;
=== [[File:FilterObservations procedure.png]]  ===&lt;br /&gt;
=== Calling FilterObservations Procedure ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+The following input parameters are passed by calling the FilterObservations procedure:&lt;br /&gt;
!Parameter&lt;br /&gt;
!Data type&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|filterType&lt;br /&gt;
|INT&lt;br /&gt;
|0 earliest, 1 latest, 2 ever, 3 pivot around cohort pivot date (6 months), 4 all Since&lt;br /&gt;
|-&lt;br /&gt;
|toCreate&lt;br /&gt;
|INT&lt;br /&gt;
|1 is the default value - to call createObservationsFromCohort&lt;br /&gt;
|-&lt;br /&gt;
|ignorenulls&lt;br /&gt;
|VARCHAR(1)&lt;br /&gt;
|Y or N - whether to ignore null result value&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS filterObservations;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE filterObservations (&lt;br /&gt;
    IN filterType INT,&lt;br /&gt;
  -- 0 earliest, 1 latest, 2 ever,&lt;br /&gt;
  -- 3 pivot around cohort pivot date (6 months),&lt;br /&gt;
  -- 4 allSince&lt;br /&gt;
    IN toCreate INT, -- 1 to createObservationsFromCohortv2&lt;br /&gt;
    IN ignorenulls VARCHAR(1) -- Y or N&lt;br /&gt;
 )&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
IF (toCreate = 1) THEN&lt;br /&gt;
CALL createObservationsFromCohort (filterType);&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS filteredObservations;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 0) THEN -- earliest&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE filteredObservations AS&lt;br /&gt;
   SELECT&lt;br /&gt;
        ob.id,&lt;br /&gt;
        ob.group_by,&lt;br /&gt;
        ob.patient_id,&lt;br /&gt;
        ob.person_id,&lt;br /&gt;
        ob.original_code,&lt;br /&gt;
        ob.original_term,&lt;br /&gt;
        ob.result_value,&lt;br /&gt;
        ob.clinical_effective_date,&lt;br /&gt;
        ob.result_value_units,&lt;br /&gt;
        ob.age_years,&lt;br /&gt;
        ob.rnk&lt;br /&gt;
    FROM (&lt;br /&gt;
        SELECT o.id,&lt;br /&gt;
               o.group_by,&lt;br /&gt;
               o.patient_id,&lt;br /&gt;
               o.person_id,&lt;br /&gt;
               o.original_code,&lt;br /&gt;
               o.original_term,&lt;br /&gt;
               o.result_value,&lt;br /&gt;
               o.clinical_effective_date,&lt;br /&gt;
               o.result_value_units,&lt;br /&gt;
               o.age_years,&lt;br /&gt;
               @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
               @curperson := o.person_id AS cur_person&lt;br /&gt;
        FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
        WHERE o.result_value IS NOT NULL&lt;br /&gt;
        ORDER BY o.person_id, o.clinical_effective_date ASC, o.id ASC -- earliest&lt;br /&gt;
        ) ob&lt;br /&gt;
      WHERE ob.rnk = 1;&lt;br /&gt;
  ELSE&lt;br /&gt;
      CREATE TABLE filteredObservations AS&lt;br /&gt;
      SELECT&lt;br /&gt;
        ob.id,&lt;br /&gt;
        ob.group_by,&lt;br /&gt;
        ob.patient_id,&lt;br /&gt;
        ob.person_id,&lt;br /&gt;
        ob.original_code,&lt;br /&gt;
        ob.original_term,&lt;br /&gt;
        ob.result_value,&lt;br /&gt;
        ob.clinical_effective_date,&lt;br /&gt;
        ob.result_value_units,&lt;br /&gt;
        ob.age_years,&lt;br /&gt;
        ob.rnk&lt;br /&gt;
      FROM (&lt;br /&gt;
        SELECT o.id,&lt;br /&gt;
               o.group_by,&lt;br /&gt;
               o.patient_id,&lt;br /&gt;
               o.person_id,&lt;br /&gt;
               o.original_code,&lt;br /&gt;
               o.original_term,&lt;br /&gt;
               o.result_value,&lt;br /&gt;
               o.clinical_effective_date,&lt;br /&gt;
               o.result_value_units,&lt;br /&gt;
               o.age_years,&lt;br /&gt;
               @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
               @curperson := o.person_id AS cur_person&lt;br /&gt;
      FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
      ORDER BY o.person_id, o.clinical_effective_date ASC, o.id ASC -- earliest&lt;br /&gt;
      ) ob&lt;br /&gt;
    WHERE ob.rnk = 1;&lt;br /&gt;
  END IF;&lt;br /&gt;
ELSEIF (filterType = 2 OR filterType = 4) THEN -- ever or allSince&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
     CREATE TABLE filteredObservations AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
         mc.group_by,&lt;br /&gt;
         mc.patient_id,&lt;br /&gt;
         mc.person_id,&lt;br /&gt;
         mc.original_code,&lt;br /&gt;
         mc.original_term,&lt;br /&gt;
         mc.result_value,&lt;br /&gt;
         mc.clinical_effective_date,&lt;br /&gt;
         mc.result_value_units,&lt;br /&gt;
         mc.age_years&lt;br /&gt;
     FROM observationsFromCohort mc&lt;br /&gt;
     WHERE mc.result_value IS NOT NULL;&lt;br /&gt;
  ELSE&lt;br /&gt;
     CREATE TABLE filteredObservations AS&lt;br /&gt;
     SELECT DISTINCT&lt;br /&gt;
         mc.group_by,&lt;br /&gt;
         mc.patient_id,&lt;br /&gt;
         mc.person_id,&lt;br /&gt;
         mc.original_code,&lt;br /&gt;
         mc.original_term,&lt;br /&gt;
         mc.result_value,&lt;br /&gt;
         mc.clinical_effective_date,&lt;br /&gt;
         mc.result_value_units,&lt;br /&gt;
         mc.age_years&lt;br /&gt;
     FROM observationsFromCohort mc;&lt;br /&gt;
  END IF;&lt;br /&gt;
ELSEIF (filterType = 1 or filterType = 3) THEN -- latest or pivot&lt;br /&gt;
  IF (ignorenulls = &#039;Y&#039;) THEN&lt;br /&gt;
    CREATE TABLE filteredObservations AS&lt;br /&gt;
    SELECT&lt;br /&gt;
        ob.id,&lt;br /&gt;
        ob.group_by,&lt;br /&gt;
        ob.patient_id,&lt;br /&gt;
        ob.person_id,&lt;br /&gt;
        ob.original_code,&lt;br /&gt;
        ob.original_term,&lt;br /&gt;
        ob.result_value,&lt;br /&gt;
        ob.clinical_effective_date,&lt;br /&gt;
        ob.result_value_units,&lt;br /&gt;
        ob.age_years,&lt;br /&gt;
        ob.rnk&lt;br /&gt;
    FROM (&lt;br /&gt;
    SELECT o.id,&lt;br /&gt;
        o.group_by,&lt;br /&gt;
        o.patient_id,&lt;br /&gt;
        o.person_id,&lt;br /&gt;
        o.original_code,&lt;br /&gt;
        o.original_term,&lt;br /&gt;
        o.result_value,&lt;br /&gt;
        o.clinical_effective_date,&lt;br /&gt;
        o.result_value_units,&lt;br /&gt;
        o.age_years,&lt;br /&gt;
        @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
        @curperson := o.person_id AS cur_person&lt;br /&gt;
    FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
    WHERE o.result_value IS NOT NULL&lt;br /&gt;
    ORDER BY o.person_id, o.clinical_effective_date DESC, o.id DESC -- latest&lt;br /&gt;
      ) ob&lt;br /&gt;
    WHERE ob.rnk = 1;&lt;br /&gt;
  ELSE&lt;br /&gt;
    CREATE TABLE filteredObservations AS&lt;br /&gt;
    SELECT&lt;br /&gt;
      ob.id,&lt;br /&gt;
      ob.group_by,&lt;br /&gt;
      ob.patient_id,&lt;br /&gt;
      ob.person_id,&lt;br /&gt;
      ob.original_code,&lt;br /&gt;
      ob.original_term,&lt;br /&gt;
      ob.result_value,&lt;br /&gt;
      ob.clinical_effective_date,&lt;br /&gt;
      ob.result_value_units,&lt;br /&gt;
      ob.age_years,&lt;br /&gt;
      ob.rnk&lt;br /&gt;
    FROM (&lt;br /&gt;
    SELECT o.id,&lt;br /&gt;
           o.group_by,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           o.original_code,&lt;br /&gt;
           o.original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           o.age_years,&lt;br /&gt;
           @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
           @curperson := o.person_id AS cur_person&lt;br /&gt;
    FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
    ORDER BY o.person_id, o.clinical_effective_date DESC, o.id DESC -- latest&lt;br /&gt;
    ) ob&lt;br /&gt;
    WHERE ob.rnk = 1;&lt;br /&gt;
&lt;br /&gt;
  END IF;&lt;br /&gt;
&lt;br /&gt;
ELSE&lt;br /&gt;
  SIGNAL SQLSTATE &#039;45000&#039;&lt;br /&gt;
  SET MESSAGE_TEXT = &#039;filterType not recognised&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Calling the createobservationsFromCohort procedure ====&lt;br /&gt;
The createobservationsFromCohort procedure is called from the FilterObservations procedure; the filter type is passed as the input parameter:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CALL createobservationsFromCohort (filterType);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;An example of the createobservationsFromCohort procedure is shown below.&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DROP PROCEDURE IF EXISTS createObservationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE PROCEDURE createObservationsFromCohort (&lt;br /&gt;
  IN filterType INT&lt;br /&gt;
  -- 0 earliest, 1 latest, 2 ever, 3 pivot around cohort_gh2 pivot date (6 months)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS snomeds_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE snomeds_tmp AS&lt;br /&gt;
SELECT cat_id,&lt;br /&gt;
       snomed_id&lt;br /&gt;
FROM snomeds&lt;br /&gt;
WHERE cat_id IN (3, 4);&lt;br /&gt;
&lt;br /&gt;
DELETE t1 FROM snomeds t1 JOIN snomeds_tmp t2&lt;br /&gt;
ON t1.snomed_id = t2.snomed_id&lt;br /&gt;
WHERE t1.cat_id IN (1, 2);&lt;br /&gt;
&lt;br /&gt;
DROP TABLE IF EXISTS observationsFromCohort;&lt;br /&gt;
&lt;br /&gt;
IF (filterType = 3) THEN&lt;br /&gt;
-- pivot over 6 months from pivot date (already set in cohort)&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE observationsFromCohort AS&lt;br /&gt;
    SELECT DISTINCT&lt;br /&gt;
           o.id,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           o.group_by,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.original_code,&lt;br /&gt;
           SUBSTRING(o.original_term, 1, 200) AS original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           o.age_years&lt;br /&gt;
    FROM cohort_observations o JOIN cohort c&lt;br /&gt;
    ON o.group_by = c.group_by&lt;br /&gt;
    WHERE EXISTS (SELECT &#039;x&#039; FROM snomeds s&lt;br /&gt;
                  WHERE s.cat_id IN (1, 2)&lt;br /&gt;
                  AND s.snomed_id = o.original_code)&lt;br /&gt;
    AND o.clinical_effective_date IS NOT NULL&lt;br /&gt;
    AND o.clinical_effective_date&lt;br /&gt;
    BETWEEN DATE_SUB(c.pivot_date, INTERVAL 6 MONTH)&lt;br /&gt;
    AND DATE_SUB(c.pivot_date, INTERVAL -6 MONTH);&lt;br /&gt;
ELSE&lt;br /&gt;
-- latest or earliest or ever (so all observations)&lt;br /&gt;
&lt;br /&gt;
  CREATE TABLE observationsFromCohort AS&lt;br /&gt;
    SELECT DISTINCT&lt;br /&gt;
           o.id,&lt;br /&gt;
           o.patient_id,&lt;br /&gt;
           o.person_id,&lt;br /&gt;
           o.group_by,&lt;br /&gt;
           o.clinical_effective_date,&lt;br /&gt;
           o.original_code,&lt;br /&gt;
           SUBSTRING(o.original_term, 1, 200) AS original_term,&lt;br /&gt;
           o.result_value,&lt;br /&gt;
           o.result_value_units,&lt;br /&gt;
           o.age_years&lt;br /&gt;
    FROM cohort_observations o&lt;br /&gt;
    WHERE EXISTS (SELECT &#039;x&#039; FROM snomeds s&lt;br /&gt;
                  WHERE s.cat_id IN (1, 2)&lt;br /&gt;
                  AND s.snomed_id = o.original_code)&lt;br /&gt;
    AND o.clinical_effective_date IS NOT NULL;&lt;br /&gt;
&lt;br /&gt;
END IF;&lt;br /&gt;
&lt;br /&gt;
ALTER TABLE observationsFromCohort ADD INDEX obv_pat_idx (patient_id);&lt;br /&gt;
&lt;br /&gt;
END//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;This returns a new observations cohort based on the SNOMED codes list.&lt;br /&gt;
&lt;br /&gt;
==== Filter Observations by Timeframe ====&lt;br /&gt;
&lt;br /&gt;
Using the new observations cohort, we pass in the filter type to retrieve the observations based on the timeframe, and using the clinical effective date to determine the time intervals. We store the result set into a temporary table. This is illustrated in the code snippet of the filterObservationsV2 procedure below:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE filteredObservations AS&lt;br /&gt;
  SELECT&lt;br /&gt;
    ob.id,&lt;br /&gt;
    ob.group_by,&lt;br /&gt;
    ob.patient_id,&lt;br /&gt;
    ob.person_id,&lt;br /&gt;
    ob.original_code,&lt;br /&gt;
    ob.original_term,&lt;br /&gt;
    ob.result_value,&lt;br /&gt;
    ob.clinical_effective_date,&lt;br /&gt;
    ob.result_value_units,&lt;br /&gt;
    ob.age_years,&lt;br /&gt;
    ob.rnk&lt;br /&gt;
FROM (&lt;br /&gt;
  SELECT o.id,&lt;br /&gt;
         o.group_by,&lt;br /&gt;
         o.patient_id,&lt;br /&gt;
         o.person_id,&lt;br /&gt;
         o.original_code,&lt;br /&gt;
         o.original_term,&lt;br /&gt;
         o.result_value,&lt;br /&gt;
         o.clinical_effective_date,&lt;br /&gt;
         o.result_value_units,&lt;br /&gt;
         o.age_years,&lt;br /&gt;
         @currank := IF(@curperson = o.person_id, @currank + 1, 1) AS rnk,&lt;br /&gt;
         @curperson := o.person_id AS cur_person&lt;br /&gt;
  FROM observationsFromCohort o, (SELECT @currank := 0, @curperson := 0) r&lt;br /&gt;
  WHERE o.result_value IS NOT NULL&lt;br /&gt;
  ORDER BY o.person_id, o.clinical_effective_date ASC, o.id ASC -- earliest&lt;br /&gt;
  ) ob&lt;br /&gt;
WHERE ob.rnk = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Updating Dataset Table Columns ===&lt;br /&gt;
Using the result set derived from the above step, the dataset table columns are updated with the appropriate observational values. &lt;br /&gt;
&lt;br /&gt;
This is illustrated in the code snippet of the populateCodeDateV2 procedure:&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TEMPORARY TABLE IF EXISTS qry_tmp;&lt;br /&gt;
&lt;br /&gt;
CREATE TEMPORARY TABLE qry_tmp (&lt;br /&gt;
  row_id                   INT,&lt;br /&gt;
  group_by                 VARCHAR(255),&lt;br /&gt;
  original_code            VARCHAR(20),&lt;br /&gt;
  original_term            VARCHAR(200),&lt;br /&gt;
  clinical_effective_date  DATE, PRIMARY KEY(row_id)&lt;br /&gt;
) AS&lt;br /&gt;
  SELECT (@row_no := @row_no+1) AS row_id,&lt;br /&gt;
    f.group_by,&lt;br /&gt;
    f.original_code,&lt;br /&gt;
    f.original_term,&lt;br /&gt;
    f.clinical_effective_date&lt;br /&gt;
  FROM filteredObservations f, (SELECT @row_no := 0) t;&lt;br /&gt;
&lt;br /&gt;
SET @row_id = 0;&lt;br /&gt;
&lt;br /&gt;
WHILE EXISTS (SELECT row_id from qry_tmp&lt;br /&gt;
              WHERE row_id &amp;gt; @row_id AND row_id &amp;lt;= @row_id + 1000) DO&lt;br /&gt;
&lt;br /&gt;
  SET @sql = CONCAT(&#039;UPDATE &#039;, datasetTable,&lt;br /&gt;
      &#039; d JOIN qry_tmp f ON d.pseudo_id = f.group_by SET &#039;,&lt;br /&gt;
      col, &amp;quot;Code = f.original_code, &amp;quot;,&lt;br /&gt;
      col, &amp;quot;Term = f.original_term, &amp;quot;,&lt;br /&gt;
      col, &amp;quot;Date = date_format(f.clinical_effective_date, &#039;%d/%m/%Y&#039;)&lt;br /&gt;
      WHERE f.row_id &amp;gt; @row_id AND f.row_id &amp;lt;= @row_id + 1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  PREPARE stmt FROM @sql;&lt;br /&gt;
  EXECUTE stmt;&lt;br /&gt;
  DEALLOCATE PREPARE stmt;&lt;br /&gt;
&lt;br /&gt;
  SET @row_id = @row_id + 1000;&lt;br /&gt;
&lt;br /&gt;
END WHILE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>AndreHui</name></author>
	</entry>
</feed>