HL7 for Busy Professionals
Page 10
OBR-22: Date/Time of Result Report or Status Change
This field contains the date/time when the result became available or the status of the result changed. In other words, it represents the report date. Its counterpart, order date is populated by the placer in the OBR-6 field. Together, OBR-6 and OBR-22 can be used to calculate the turnaround time for an order.
OBR-24: Diagnostic Service ID
This is another commonly populated field in the result message. It represents the department that performed the test. The field contains a two or three letter code for various services. You will see values like MB (microbiology) or RAD (Radiology) in this field.
OBR-25: Result Status
This field holds the status of the result and is a required field in the OBR segment of the result message. It is an important field and if you are going to be working with result messages, this is one field you will be looking up frequently.
An important thing to keep in mind is that this field represents the status of a particular order set and not all the orders in a message. There could be result for multiple order sets in a result message and because of that there will be multiple OBR segments in the message. Each OBR-25 represents only the overall result status for its own order set.
OBX – Observation Result Segment
If the OBR segment is like the header of an ordered result then the OBX segment is the body of the result and holds the actual result data. Since one OBX segment can hold information only for a single observation, each OBR segment is generally associated with multiple OBX segments.
OBX-1: Set ID
Unlike other segments that have Set ID, it is a useful field in the OBX segment. It is the sequence number, which goes up by one for each successive OBX under an OBR. For OBXs associated with another OBR, the numbering restarts at one. This makes it easy to read a raw HL7 message.
OBX-2: Value Type
This field defines the data type of the result field (OBX-5). If the result is in the form of a narrative report, then the value in this field is always TX (code for text data). For structured reports, the value is usually CE (coded Entry) but it is possible that you may run into a different value.
OBX-3: Observation Identifier
If you remember, order sets and individual orders are represented by codes in an HL7 message. Usually LOINC codes are used to represent these orders.
The code for the overall order set is in the OBR-4 field but for individual orders that make up the order set, their codes are in the OBX-3 field. Each OBX-3 holds the code for one individual order.
OBX-5: Observation Value
This is the field that holds the result of the test. There is no limit on the length of this field. For narrative reports, this is where the sentences of the report go. Sentences are broken up into multiple OBX’s so that it looks like a nicely formatted paragraph.
OBX-6: Units
This field identifies the unit of measurement for values in the OBX-5 field. If something is being reported as “1 ml” then "1" will be in the OBX-5 field and "ml" will be in the OBX-6 field.
OBX-11: Observation Result Status
This is a required field that is very important. It contains the code for the status of the result. This field is used extensively in managing records such as updating an old record, correcting a wrong result or marking a result as final.
In real life, you don’t just get one message with the result of the ordered test. It is more typical to first receive a preliminary result. Then, maybe there is a correction to that test and a corrected result is sent. The lab may decide to run the tests again and issue a final result. All these results are sent one after the other. So, it is quite possible that an order will spawn two or three result messages.
A value of “P” in this field means the result is “Preliminary”. “C” means the result is a "correction" of a previously reported result and “F” means this is the final value of the result.
10. Other Important Topics
We haven’t touched on some important HL7 topics yet. But that is intentional. They are important to understanding HL7, but in my opinion, throwing a newcomer off the deep end is not the right way to teach someone to swim. Learning doesn’t have to be a frightening experience.
So here at the deep end, let’s first look at data types. Data types are an integral part of HL7. There is a whole chapter (2A) dedicated to them in the HL7 spec.
HL7 Data Type
In HL7 you have your usual data types such as text and numeric but then you also have data types for name, address, visiting hours, frequency of medication and other values that are not normally considered a data type.
If you are thinking that the data type for name should be just text, then you do have a point. However, in HL7, data types take on a bigger role. They are used to gain a very fine control over the structure of a field - how it is defined, and to impose restrictions on its content. For regular data types, you only have restriction on the type of value (Boolean, text or numeric). With HL7, the restrictions go way beyond just the type of value. There are restrictions on the length, on how the content is organized, when a value has to be present etc.
Remember the building blocks of a message? A message is a collection of segments, which is a collection of fields, which is a collection of components and so on. Data types come into play at the field level. Whether a field has components and sub-components depends entirely on the data type of that field. If a data type has five components and two of those components are further made up of subcomponents, then the field inherits that property. It can be said that the field has five components and two of those have subcomponents.
If you look at the attribute table of any segment, the third column with the heading DT is the data type column. It contains the data type code for fields in that segment. The codes are usually two or three characters in length and are always in uppercase.
Chapter 2A of the specification document defines all the data types in HL7. For details of a particular data type, you will need to look in Chapter 2A, where they are listed in the alphabetical order.
I consider it highly unlikely but if you do spend time browsing Chapter 2A, you will notice that some data types are very simple. They have just one component. These are the familiar data types - ST (String), NM (Numeric), TX (Text) etc. Others data types use these basic types to form more complex types and then you have the scary ones which are made up of both simple and complex data types. They are the poster children for what scares people away from HL7.
I like to organize data types into basic, intermediate and complex categories. Basic is the simplest data type with just one component. Then you have the intermediate types with multiple components of basic type and finally, there is the complex data type, which is a mix of both basic and intermediate types.
Let’s look at an example of each.
NM – Numeric Data Type
This is an example of a basic data type. A field with this data type can only have numeric values with a maximum length of sixteen characters. It also allows a leading sign (+/-) and a decimal point within the value. (Examples: 21 and +33.90).
NR – Numeric Range Data Type
This is an intermediate level data type. It has two components, both of which are basic NM data types. A field with this type defines a range of value by specifying the lowest possible and the highest possible value in the range.
As you can see in the table, the first component is always the low value and both components are optional. For example if a field looks like 70^110 then the value ranges from a low of 70 to a high of 110.
PTA – Policy type and amount
This is an example of a complex data type. There are multiple components where some have their own components (MOP). A couple of components also have restrictions on the value. Look at column TBL#. The first two components can only have the values listed in those tables.
But I am not giving you a true picture of complex data types with this example. Many of
them, like XCN and XAD, are truly monstrous and contain tens of components and sub-components.
Using Data Types
So far we have only dealt with the theory behind data types. In practice, we don’t deal with all this complexity. Let’s take one complex data type and see how it is really used in a real world application.
Imagine you are mapping the PID segment for a message. So now you have to get all the patient related information mapped out. When you come to the PID-5 field, you see that it’s the patient name field and the data type is XPN. This means the patient’s name has to be written according to XPN data type requirements. You cannot just drop the name as one long string. The receiving system will reject the message.
So how do you map the patient’s name? This information will be in the interface spec and interface specs are based on HL7 specs. So more often than not, data types are defined exactly accordingly to HL7 spec. Assuming that is the case, lets head to Chapter 2A and scroll down to where the component table for the XPN data type is defined.
What we have here is a data type with fourteen components. A look at the fourth column (OPT), which defines whether a component is required or optional, tells us that everything is optional. (O is optional and B is backward compatibility/previous version, ignore B). This is what makes life easy.
If the name of the patient is “Tommy Boy” then all you will need to populate are the first two components as Boy^Tommy. You can ignore the rest.
Why is that? Because XPN data type requires the first component to be the family name. Then you have a component separator ^ followed by the given name in the second component. That’s why Tommy Boy’s name is written as Boy^Tommy.
By leaving out the rest of the components in the name field, we are indicating that the remaining components are empty.
Let’s add a little twist to the name. Say, Tommy Boy’s full name is “Tommy Boy Jr.”. Now there is a suffix in the name and if you refer to the table above, suffix should go in the fourth component. In this case, the name will be written as Boy^Tommy^^Jr. There are two carets side by side (^^) in this name. This is to indicate that the third component is empty and “Jr.” is the value in the fourth component.
Do you see now why I said you don’t have to deal with all the complexity? Most names are simple and that means only the first three or four components are populated and we ignore the rest. The same is true for other data types. If you do come across an unfamiliar component populated in a field, a quick visit to Chapter 2A will solve the problem.
Also, the 80-20 rule works very well with HL7 data types. Only a handful of data types are commonly used. We can safely ignore the vast majority. Here are a few commonly used examples.
Coded Element (CE)
This is a very common data type in HL7, but to the uninitiated, it means nothing. A field with this data type can only have a coded value. An example would be LOINC code for lab result.
When lab tests are reported electronically, they don’t use long descriptive sentences to report their findings. Instead, alphanumeric codes are used to represent the type of test. It is these codes along with the result values that are sent across in an HL7 message. A field with a CE data type means it can only contain valid codes defined by a coding system (like LOINC). You can also define your own local codes and use that in a CE field.
There are a couple of variations to this data type. CNE (Coded with No Exception) data type means the field can only have those codes that are defined by the coding system. CWE (Coded With Exception), on the other hand, allows codes to be defined locally in order to extend the coding system.
Coded Value (ID – for HL7 tables; IS – for user tables)
Whenever you come across a field with an ID or IS data type, it means there is a table linked to this field. You can find the table number in the attribute table for that data type. It is a four digit number under the TBL# column. In HL7, every table is assigned a unique four digit table number.
The reason we have tables is because there are many fields where it is necessary to define a standard set of values. For example, consider the case of the “sex” field. We can say that valid values in this field are male, female and unknown. But if the text is not standardized, it will lead to all kinds of variations. Guy and gal, for example.
For some of the fields, HL7 sets the values and defines the table. An example is Table 0003, which contains a list of all valid event codes. The EVN-1 field can only take values from this table.
For others, HL7 defines a suggested list of values in a table. It is left to the sites to decide if they want to use those values, modify them or create their own values. Table 0001 (Administrative Sex) is a good example.
And finally, there are fields like “Pre-Admit Test Indicator” (PV1-12) where the values can only be defined locally. HL7 only assigns the table number (0087).
Merge Messages
Merge messages are a subset of ADT that deal specifically with merging patient records in a database. These messages are for record housekeeping but that should not lull you into thinking that they are not important. Far from it. They are used often and chances are high that you will encounter them someday. Any HL7 expert worth his/her salt should understand merge. In fact, that is how we used to test the level of expertise of a new colleague.
There are three kinds of operations where merge messages are used: merging the content of two records into one, moving a child record from one place to another and changing the ID of a record.
Before we go any further, let’s recap how health records are organized. At the very top is the person record. A person could be a patient, a relative, a doctor, etc. If the person is a patient, then the patient record sits under the person record. Each patient record can have one or more account records to track resources used (for example, an account for recurring dialysis visits and a separate one for an emergency visit). Each account can have one or more patient visit records linked to it. Visually this is what the hierarchy will look like.
There are situations where this neat hierarchy of records gets tangled up. Consider the case of Mr. Rocky Racoon. He pays a visit to the hospital with a dislocated shoulder. The registration clerk looks up the hospital system to see if he has a record. None are found, so a new patient record is created for Rocky. This is where the problem starts. Rocky has been to that hospital before and has an existing patient record. The registration clerk searched for his name incorrectly even though Rocky told him specifically that he spells his last name with a single c - Racoon and not Raccoon. Of course, the system did not find his name, and now, there are two patient records for Rocky. If only we could take the human factor out!
This is a case of multiple records for the same person and it is resolved by merging the new record with Rocky’s earlier patient record. Basically, it means moving the account and visit records from under the new patient record to the old patient record and deleting the new record.
A different situation could be that there is another guy Rocky Raccoon and he does spell his name with a double c. In that case, our Rocky's account and visit information is attached to the wrong patient record. This is resolved by moving the account and visit records to the correct patient record. This is a move operation.
Finally, there is the situation of an incorrect identifier assigned to a record. This doesn't happen anymore because identifiers are assigned automatically by the systems. But back in the days when identifiers were manually created, this was a real issue. Say, a hospital required all patient numbers to be seven characters in length and start with an alphabet. A new employee, not knowing any better, creates a record where patient number is only five characters in length and doesn't start with an alphabet. This will be a change operation.
Now that we understand what is involved in a merge operation, let's see how HL7 messages facilitate this. Altogether, eighteen different trigger events are defined for merge, move and change operations, half of those are just legacy triggers. HL7 is a lot like the DNA, t
here are many base pairs that do nothing but probably were useful at some point during evolution.
Trigger events A18, A30, A34, A35, A36, A39, A46 and A48 are there to maintain backward compatibility. Of the rest, three trigger events are for merge operation (A40, A41 & A42), three for move (A43, A44 & A45) and the remaining four for change operation (A47, A49, A50 & A51). Merge operations are the most important and frequently used, so we will take a closer look at them.
There are three different merge messages - event A40 is for merging at the patient record level, A41 is for merging at the account level and A42 is for merging at the visit level.
All three have similar message structures and the most important segment in these messages is the merge segment (MRG). It has three important fields: MRG-1 (prior patient ID), MRG-3 (prior account number) and MRG-5 (prior visit number). These fields identify records that have to be merged.
This is how the merge process works.
Every merge message has a PID and an MRG segment (and a PV1 segment if the merge is at the visit level). Their content includes the identifiers of the records to be merged.
The record, which will continue to exist, is called the surviving record and its identifier is in the PID segment. The record that will be merged is the non-surviving (or prior) record and its identifier is in the MRG segment.
For a merge at the patient record level, the surviving patient ID is in the PID-3 field and the non-surviving ID is in the MRG-1 field. Other MRG fields stay empty. This tells the receiving application to keep the patient ID in PID-3 and move records under MRG-1 to PID-3. How records are moved is decided by the receiving application. This is a database operation and HL7 leaves it at the discretion of the implementers.