Using Caché Globals
Using Multidimensional Storage (Globals)
[Back] [Next]
   
Server:docs2
Instance:LATEST
User:UnknownUser
 
-
Go to:
Search:    

This chapter describes the various operations you can perform using multidimensional storage (global variables). It includes the following topics:

Storing Data in Globals
Storing data in global nodes is simple: you treat a global as you would any other variable. The difference is that operations on globals are automatically written to the database.
Creating Globals
There is no setup work required to create a new global; simply setting data into a global implicitly creates a new global structure. You can create a global (or a global subscript) and place data in it with a single operation, or you can create a global (or subscript) and leave it empty by setting it to the null string. In Caché ObjectScript, these operations are done using the SET command.
The following examples define a global named Color (if one does not already exist) and associate the value “Red” with it. If a global already exists with the name Color, then these examples modify it to contain the new information.
In Caché Basic:
^Color = "Red"
In Caché ObjectScript:
 SET ^Color = "Red"
Note:
When using direct global access within applications, develop and adhere to a naming convention to keep different parts of an application from “walking over” one another; this is similar to developing naming convention for classes, method, and other variables. Also, avoid certain global names that Caché uses; for a list of these, see the section Global Variable Names to Avoid in the “Rules and Guidelines for Identifiers” appendix of the Caché Programming Orientation Guide.
Storing Data in Global Nodes
To store a value within a global subscript node, simply set the value of the global node as you would any other variable array. If the specified node did not previously exist, it is created. If it did exist, its contents are replaced with the new value.
You specify a node within a global by means of an expression (referred to as a global reference). A global reference consists of the caret character (^), the global name, and (if needed) one or more subscript values. Subscripts (if present) are enclosed within parentheses “( )” and are separated by commas. Each subscript value is itself an expression: a literal value, a variable, a logical expression, or even a global reference.
Setting the value of a global node is an atomic operation: It is guaranteed to succeed and you do not need to use any locks to ensure concurrency.
The following are all valid global references:
In Caché Basic:
^Data = 2
^Data("Color") = "Red"
^Data(1,1) = 100
^Data(^Data) = 10     ' The value of ^Data is the subscript
^Data(a,b) = 50       ' The values of local variables a and b are subscripts
^Data(a + 10) = 50    
In Caché ObjectScript:
   SET ^Data = 2
   SET ^Data("Color")="Red"
   SET ^Data(1,1)=100        /* The 2nd-level subscript (1,1) is set
                                to the value 100. No value is stored at
                                the 1st-level subscript (^Data(1)). */   
   SET ^Data(^Data)=10       /* The value of global variable ^Data 
                                is the name of the subscript. */
   SET ^Data(a,b)=50         /* The values of local variables a and b
                                are the names of the subscripts. */
   SET ^Data(a+10)=50       
If you are using Caché ObjectScript, you can construct global references at runtime using indirection.
Storing Structured Data in Global Nodes
Each global node can contain a single string of up to 32K characters.
Data is typically stored within nodes in one of the following ways:
Deleting Global Nodes
To remove a global node, a group of subnodes, or an entire global from the database, use the Caché ObjectScript KILL or ZKILL commands, or the Caché Basic Erase command.
The KILL command deletes all nodes (data as well as its corresponding entry in the array) at a specific global reference, including any descendant subnodes. That is, all nodes starting with the specified subscript are deleted.
For example, the Caché ObjectScript statement:
  KILL ^Data
deletes the entire ^Data global. A subsequent reference to this global would return an <UNDEFINED> error.
The Caché ObjectScript statement:
   KILL ^Data(100)
deletes contents of node 100 within the ^Data global. If there are descendant subnodes, such as ^Data(100,1), ^Data(100,2), and ^Data(100,1,2,3), these are deleted as well.
The Caché ObjectScript ZKILL command deletes a specified global or global subscript node. It does not delete descendant subnodes.
Note:
Following the kill of a large global, the space once occupied by that global may not have been completely freed, since the blocks are marked free in the background by the Garbage Collector daemon. Thus, a call to the ReturnUnusedSpace method of the SYS.Database class immediately after killing a large global may not return as much space as expected, since blocks occupied by that global may not have been released as yet.
You cannot use the NEW command on global variables.
Testing the Existence of a Global Node
To test if a specific global (or its descendants) contains data, use the Caché ObjectScript $DATA function.
$DATA returns a value indicating whether or not the specified global reference exists. The possible return values are:
Status Value Meaning
0 The global variable is undefined.
1 The global variable exists and contains data, but has no descendants. Note that the null string ("") qualifies as data.
10 The global variable has descendants (contains a downward pointer to a subnode) but does not itself contain data. Any direct reference to such a variable will result in an <UNDEFINED> error. For example, if $DATA(^y) returns 10, SET x=^y will produce an <UNDEFINED> error.
11 The global variable both contains data and has descendants (contains a downward pointer to a subnode).
Retrieving the Value of a Global Node
To get the value stored within a specific global node, simply use the global reference as an expression.
Using Caché Basic:
color = ^Data("Color")   ' assign to a local variable
Print ^Data("Color")     ' use as an argument to a command
MyMethod(^Data("Color")) ' use as a function argument
Using Caché ObjectScript:
   SET color = ^Data("Color")    ; assign to a local variable
   WRITE ^Data("Color")          ; use as a command argument
   SET x=$LENGTH(^Data("Color")) ; use as a function parameter
The $GET Function
Within Caché ObjectScript, you can also get the value of a global node using the $GET function:
   SET mydata = $GET(^Data("Color"))
This retrieves the value of the specified node (if it exists) or returns the null string ("") if the node has no value. You can use the optional second argument of $GET to return a specified default value if the node has no value.
The WRITE, ZWRITE, and ZZDUMP Commands
You can display the contents of a global or a global subnode by using the various Caché ObjectScript display commands. The WRITE command returns the value of the specified global or subnode as a string. The ZWRITE command returns the name of the global variable and its value, and each of its descendant nodes and their values. The ZZDUMP command returns the value of the specified global or subnode in hexadecimal dump format.
Traversing Data within a Global
There are a number of ways to traverse (iterate over) data stored within a global.
The $ORDER (Next / Previous) Function
The Caché ObjectScript $ORDER function (and its Caché Basic equivalent: Traverse) allows you to sequentially visit each node within a global.
The $ORDER function returns the value of the next subscript at a given level (subscript number). For example, suppose you have defined the following global:
 Set ^Data(1) = ""
 Set ^Data(1,1) = ""
 Set ^Data(1,2) = ""
 Set ^Data(2) = ""
 Set ^Data(2,1) = ""
 Set ^Data(2,2) = ""
 Set ^Data(5,1,2) = ""
To find the first, first-level subscript, we can use:
 SET key = $ORDER(^Data(""))
This returns the first, first-level subscript following the null string (""). (The null string is used to represent the subscript value before the first entry; as a return value it is used to indicate that there are no following subscript values.) In this example, key will now contain the value 1.
We can find the next, first-level subscript by using 1 or key in the $ORDER expression:
 SET key = $ORDER(^Data(key))
If key has an initial value of 1, then this statement will set it to 2 (as ^Data(2) is the next first-level subscript). Executing this statement again will set key to 5 as that is the next first-level subscript. Note that 5 is returned even though there is no data stored directly at ^Data(5). Executing this statement one more time will set key to the null string (""), indicating that there are no more first level subscripts.
By using additional subscripts with the $ORDER function, you can iterate over different subscript levels. $ORDER returns the next value of the last subscript in its argument list. Using the data above, the statement:
 SET key = $ORDER(^Data(1,""))
will set key to 1 as ^Data(1,1) is the next second-level subscript. Executing this statement again will set key to 2 as that is the next second-level subscript. Executing this statement one more time will set key to “” indicating that there are no more second-level subscripts under node ^Data(1).
Looping with $ORDER
The following Caché ObjectScript code defines a simple global and then loops over all of its first-level subscripts:
 // clear ^Data in case it has data
 Kill ^Data

 // fill in ^Data with sample data
 For i = 1:1:100 {
     // Set each node to a random person's name
     Set ^Data(i) = ##class(%PopulateUtils).Name()
 }

 // loop over every node
 // Find first node
 Set key = $Order(^Data(""))

 While (key '= "") {
     // Write out contents
     Write "#", key, " ", ^Data(key),!

     // Find next node
     Set key = $Order(^Data(key))
 }
 
Additional $ORDER Arguments
The Caché ObjectScript $ORDER function takes optional second and third arguments. The second argument is a direction flag indicating in which direction you wish to traverse a global. The default, 1, specifies forward traversal, while –1 specifies backward traversal.
The third argument, if present, contains a local variable name. If the node found by $ORDER contains data, the data found is written into this local variable. When you are looping over a global and you are interested in node values as well as subscript values, this operates more efficiently.
Looping Over a Global
If you know that a given global is organized using contiguous numeric subscripts, you can use a simple For loop to iterate over its values. For example, in Caché Basic:
For i = 1 To 100
    Print ^Data(i)
Next
or the equivalent in Caché ObjectScript:
 For i = 1:1:100 {
     Write ^Data(i),!
 }
Generally, it is better to use the $ORDER function described above: it is more efficient and you do not have to worry about gaps in the data (such as a deleted node).
The $QUERY Function
If you need to visit every node and subnode within a global, moving up and down over subnodes, use the Caché ObjectScript $QUERY function. (Alternatively you can use nested $ORDER loops).
The $QUERY function takes a global reference and returns a string containing the global reference of the next node in the global (or "" if there are no following nodes). To use the value returned by $QUERY, you must use the Caché ObjectScript indirection operator (@).
For example, suppose you define the following global:
 Set ^Data(1) = ""
 Set ^Data(1,1) = ""
 Set ^Data(1,2) = ""
 Set ^Data(2) = ""
 Set ^Data(2,1) = ""
 Set ^Data(2,2) = ""
 Set ^Data(5,1,2) = ""
The following call to $QUERY:
 SET node = $QUERY(^Data(""))
sets node to the string “^Data(1)”, the address of the first node within the global. Then, to get the next node in the global, call $QUERY again and use the indirection operator on node:
 SET node = $QUERY(@node)
At this point, node contains the string “^Data(1,1)”.
The following example defines a set of global nodes and then walks over them using $QUERY, writing the address of each node as it does:
 Kill ^Data // make sure ^Data is empty

 // place some data into ^Data
 Set ^Data(1) = ""
 Set ^Data(1,1) = ""
 Set ^Data(1,2) = ""
 Set ^Data(2) = ""
 Set ^Data(2,1) = ""
 Set ^Data(2,2) = ""
 Set ^Data(5,1,2) = ""

 // now walk over ^Data
 // find first node
 Set node = $Query(^Data(""))
 While (node '= "") {
     Write node,!
     // get next node
     Set node = $Query(@node)
 }
 
Copying Data within Globals
To copy the contents of a global (entire or partial) into another global (or a local array), use the Caché ObjectScript MERGE command.
The following example demonstrates the use of the MERGE command to copy the entire contents of the OldData global into the NewData global:
 Merge ^NewData = ^OldData
If the source argument of the MERGE command has subscripts then all data in that node and its descendants are copied. If the destination argument has subscripts, then the data is copied using the destination address as the top level node. For example, the following code:
 Merge ^NewData(1,2) = ^OldData(5,6,7)
copies all the data at and beneath ^OldData(5,6,7) into ^NewData(1,2).
Maintaining Shared Counters within Globals
A major concurrency bottleneck of large-scale transaction processing applications can be the creation of unique identifier values. For example, consider an order processing application in which each new invoice must be given a unique identifying number. The traditional approach is to maintain some sort of counter table. Every process creating a new invoice waits to acquire a lock on this counter, increments its value, and unlocks it. This can lead to heavy resource contention over this single record.
To deal with this issue, Caché provides the Caché ObjectScript $INCREMENT function. $INCREMENT atomically increments the value of a global node (if the node has no value, it is set to 1). The atomic nature of $INCREMENT means that no locks are required; the function is guaranteed to return a new incremented value with no interference from any other process.
You can use $INCREMENT as follows. First, you must decide upon a global node in which to hold the counter. Next, whenever you need a new counter value, simply invoke $INCREMENT:
 SET counter = $INCREMENT(^MyCounter)
The default storage structure used by Caché objects and SQL uses $INCREMENT to assign unique object (row) identifier values.
Using Temporary Globals
For certain operations, you may need the power of globals without requiring persistence. For example, you may want to use a global to sort some data which you do not need to store to disk. For these operations, Caché provides temporary globals.
Temporary globals have the following characteristics:
By default, Caché defines any global whose name starts with “CacheTemp” as being a temporary global. To avoid conflict with any temporary globals that Caché itself may use, you should start your temporary global names with “CacheTempUser”.
Caché SQL uses temporary globals as scratch space for optimizing complex queries. It may also uses temporary globals as temporary indices during the execution of certain queries (for sorting, grouping, calculating aggregates, etc.)
Sorting Data within Globals
Data stored within globals is automatically sorted according to the value of the subscripts. For example, the following Caché ObjectScript code defines a set of globals (in random order) and then iterates over them to demonstrate that the global nodes are automatically sorted by subscript:
 // Erase any existing data
 Kill ^Data
 
 // Define a set of global nodes
 Set ^Data("Cambridge") = ""
 Set ^Data("New York") = ""
 Set ^Data("Boston") = ""
 Set ^Data("London") = ""
 Set ^Data("Athens") = ""

 // Now iterate and display (in order)
 Set key = $Order(^Data(""))
 While (key '= "") {
     Write key,!
     Set key = $Order(^Data(key)) // next subscript
 }
 
Applications can take advantage of the automatic sorting provided by globals to perform sort operations or to maintain ordered, cross-referenced indices on certain values. Caché SQL and ObjectScript use globals to perform such tasks automatically.
Collation of Global Nodes
The order in which the nodes of a global are sorted (referred to as collation) is controlled at two levels: within the global itself and by the application using the global.
At the application level, you can control how global nodes are collated by performing data transformations on the values used as subscripts (Caché SQL and objects do this via user-specified collation functions). For example, if you wish to create a list of names that is sorted alphabetically but ignores case, then typically you use the uppercase version of the name as a subscript:
 // Erase any existing data
 Kill ^Data
 
 // Define a set of global nodes for sorting
 For name = "Cobra","jackal","zebra","AARDVark" {
     // use UPPERCASE name as subscript
     Set ^Data($ZCONVERT(name,"U")) = name
 }

 // Now iterate and display (in order)
 Set key = $Order(^Data(""))
 While (key '= "") {
     Write ^Data(key),!  // write untransformed name
     Set key = $Order(^Data(key)) // next subscript
 }
 
This example converts each name to uppercase (using the $ZCONVERT function) so that the subscripts are sorted without regard to case. Each node contains the untransformed value so that the original value can be displayed.
Numeric and String-Valued Subscripts
Numeric values are collated before string values; that is a value of 1 comes before a value of “a”. You need to be aware of this fact if you use both numeric and string values for a given subscript. If you are using a global for an index (that is, to sort data based on values), it is most common to either sort values as numbers (such as salaries) or strings (such as postal codes).
For numerically collated nodes, the typical solution is to coerce subscript values to numeric values using the unary + operator. For example, if you are building an index that sort id values by age, you can coerce age to always be numeric:
 Set ^Data(+age,id) = ""
If you wish to sort values as strings (such as “0022”, “0342”, “1584”) then you can coerce the subscript values to always be strings by prepending a space (“ ”) character. For example, if you are building an index that sort id values by zipcode, you can coerce zipcode to always be a string:
 Set ^Data(" "_zipcode,id) = ""
This ensures that values with leading zeroes, such as “0022” are always treated as strings.
The $SORTBEGIN and $SORTEND Functions
Typically you do not have to worry about sorting data within Caché. Whether you use SQL or direct global access, sorting is handled automatically.
There are, however, certain cases where sorting can be done more efficiently. Specifically, in cases where (1) you need to set a large number of global nodes that are in random (that is, unsorted) order and (2) the total size of the resulting global approaches a significant portion of the Caché buffer pool, then performance can be adversely affected — since many of the SET operations involve disk operations (as data does not fit in the cache). This scenario usually arises in cases involving the creation of index globals such as bulk data loads, index population, or sorting of unindexed values in temporary globals.
To handle these cases efficiently, Caché ObjectScript provides the $SORTBEGIN and $SORTEND functions. The $SORTBEGIN function initiates a special mode for a global (or part thereof) in which data set into the global is written to a special scratch buffer and sorted in memory (or temporary disk storage). When the $SORTEND function is called at the end of the operation, the data is written to actual global storage sequentially. The overall operation is much more efficient as the actual writing is done in an order requiring far fewer disk operations.
The $SORTBEGIN function is quite easy to use; simply invoke it with the name of the global you wish to sort before beginning the sort operation and call $SORTEND when the operation is complete:
 // Erase any existing data
 Kill ^Data

 // Initiate sort mode for ^Data global
 Set ret = $SortBegin(^Data)

 // Write random data into ^Data
 For i = 1:1:10000 {
     Set ^Data($Random(1000000)) = ""
 }

 Set ret = $SortEnd(^Data)

 // ^Data is now set and sorted

 // Now iterate and display (in order)
 Set key = $Order(^Data(""))
 While (key '= "") {
     Write key,!
     Set key = $Order(^Data(key)) // next subscript
 }
 
The $SORTBEGIN function is designed for the special case of global creation and must be used with some care. Specifically, you must not read from the global to which you are writing while in $SORTBEGIN mode; as the data is not written, reads will be incorrect.
Caché SQL automatically uses these functions for creation of temporary index globals (such as for sorting on unindexed fields).
Using Indirection with Globals
By means of indirection, Caché ObjectScript provides a way to create global references at runtime. This can be useful in applications where you do not know global structure or names at program compilation time.
Indirection is supported via the indirection operator, @, which de-references a string containing an expression. There are several types of indirection, based on how the @ operator is used.
The following code provides an example of name indirection in which the @ operator is used to de-reference a string containing a global reference:
 // Erase any existing data
 Kill ^Data

 // Set var to an global reference expression
 Set var = "^Data(100)"

 // Now use indirection to set ^Data(100)
 Set @var = "This data was set indirectly."

 // Now display the value directly:
 Write "Value: ",^Data(100)
 
You can also use subscript indirection to mix expressions (variables or literal values) within indirect statements:
 // Erase any existing data
 Kill ^Data

 // Set var to a subscript value
 Set glvn = "^Data"

 // Now use indirection to set ^Data(1) to ^Data(10)
 For i = 1:1:10 {
     Set @glvn@(i) = "This data was set indirectly."
 }

 // Now display the values directly:
 Set key = $Order(^Data(""))
 While (key '= "") {
     Write "Value ",key, ": ", ^Data(key),!
     Set key = $Order(^Data(key))
 }
 
Indirection is a fundamental feature of Caché ObjectScript; it is not limited to global references. For more information, refer to the Indirection section in the “Operators” chapter of Using Caché ObjectScript. Indirection is less efficient than direct access, so you should use it judiciously.
Managing Transactions
Caché provides the primitive operations needed to implement full transaction processing using globals. Caché objects and SQL make use of these features automatically. If you are directly writing transactional data into globals, you can make use of these operations.
The transaction commands are TSTART, which defines the start of a transaction; TCOMMIT, which commits the current transaction; and TROLLBACK, which aborts the current transaction and undoes any changes made to globals since the start of the transaction.
For example, the following Caché ObjectScript code defines the start of a transaction, sets a number of global nodes, and then commits or rolls back the transaction depending on the value of ok:
 TSTART

 Set ^Data(1) = "Apple"
 Set ^Data(2) = "Berry"

 If (ok) {
     TCOMMIT
 }
 Else {
     TROLLBACK
 }
The TSTART writes a transaction start marker in the Caché journal file. This defines the starting boundary of the transaction. If the variable ok is true (nonzero) in the above example, then the TCOMMIT command marks the successful end of the transaction and a transaction completion marker is written to the journal file. If ok is false (0), then the TROLLBACK command will undo every set or kill operation made since the start of the transaction. In this case, ^Data(1) and ^Data(2) are restored to their previous values.
Note that no data is written at the successful completion of a transaction. This is because all modifications to the database during a transaction are carried out as normal during the course of a transaction. Only in the case of a rollback is the data in the database affected. This implies that the transaction in this example has limited isolation; that is, other processes can see the modified global values before the transaction is committed. This is typically referred to as an uncommitted read. Whether this is good or bad depends on application requirements; in many cases this is perfectly reasonable behavior. If an application requires a higher degree of isolation, then this is accomplished by using locks. This is described in the following section.
Locks and Transactions
To create isolated transactions—that is, to prevent other processes from seeing modified data before a transaction is committed—requires the use of locks. Within Caché ObjectScript, you can directly acquire and release locks by means of the LOCK command. Locks work by convention; for a given data structure (such as used for a persistent object) all code that requires locks uses the same logical lock reference (that is, the same address is used by the LOCK command).
Within a transaction, locks have a special behavior; any locks acquired during the course of a transaction are not released until the end of the transaction. To see why this is, consider the actions carried out by typical transaction:
  1. Start the transaction using TSTART.
  2. Acquire a lock (or locks) on the node (or nodes) you wish to modify. This is usually referred to as a “write” lock.
  3. Modify the node (or nodes).
  4. Release the lock (or locks). Because we are in a transaction, these locks are not actually released at this time.
  5. Commit the transaction using TCOMMIT. At this point, all the locks released in the previous step are actually released.
If another process wants to look at the nodes involved in this transaction and does not want to see uncommitted modifications, then it simply tests for a lock (referred to a “read” lock) before reading the data from the nodes. Because the write locks are held until the end of the transaction, the reading process does not see the data until the transaction is complete (committed or rolled back).
Most database management systems use a similar mechanism to provide transaction isolation. Caché is unique in that it makes this mechanism available to developers. This makes it possible to create custom database structure for new application types while still supporting transactions. Of course, you can simply use Caché objects or SQL to manage your data and let your transactions be managed automatically.
Nested Calls to TSTART
Caché maintains a special system variable, $TLEVEL, that tracks how many times the TSTART command has been called. $TLEVEL starts with a value of 0; each call to TSTART increments the value of $TLEVEL by 1, while each call to TCOMMIT decrements its value by 1. If a call to TCOMMIT results in setting $TLEVEL back to 0, the transaction ends (with a commit).
A call to the TROLLBACK command always terminates the current transaction and sets $TLEVEL back to 0, regardless of the value of $TLEVEL.
This behavior gives applications the ability to wrap transactions around code (such as object methods) that itself contains a transaction. For example, the %Save method, provided by persistent objects, always performs its operation as a transaction. By explicitly calling TSTART and TCOMMIT you can create a larger transaction that encompasses several object save operations:
 TSTART
 Set sc = object1.%Save()
 If ($$$ISOK(sc)) {
     // first save worked, do the second
    Set sc = object2.%Save()
 }

 If ($$$ISERR(sc)) {
     // one of the saves failed, rollback
     TROLLBACK
 }
 Else {
     // everything is ok, commit
  TCOMMIT
 }
Managing Concurrency
The operation of setting or retrieving a single global node is atomic; it is guaranteed to always succeed with consistent results. For operations on multiple nodes or for controlling transaction isolation (see the section on Lock and Transactions), Caché provides the ability to acquire and release locks.
Locks are managed by the Caché Lock Manager. Within Caché ObjectScript, you can directly acquire and release locks by means of the LOCK command. (Caché objects and SQL automatically acquire and release locks as needed).
For details on the LOCK command, refer to the LOCK command reference page.
Checking the Most Recent Global Reference
The most recent global reference is recorded in the Caché ObjectScript $ZREFERENCE special variable. $ZREFERENCE contains the most recent global reference, including subscripts and extended global reference, if specified. Note that $ZREFERENCE indicates neither whether the global reference succeeded, nor if the specified global exists. Caché simply records the most recently specified global reference.
Naked Global Reference
Following a subscripted global reference, Caché sets a naked indicator to that global name and subscript level. You can then make subsequent references to the same global and subscript level using a naked global reference, omitting the global name and higher level subscripts. This streamlines repeated references to the same global at the same (or lower) subscript level.
Specifying a lower subscript level in a naked reference resets the naked indicator to that subscript level. Therefore, when using naked global references, you are always working at the subscript level established by the most recent global reference.
The naked indicator value is recorded in the $ZREFERENCE special variable. The naked indicator is initialized to the null string. Attempting a naked global reference when the naked indicator is not set results in a <NAKED> error. Changing namespaces reinitializes the naked indicator. You can reinitialize the naked indicator by setting $ZREFERENCE to the null string ("").
In the following example, the subscripted global ^Produce(“fruit”,1) is specified in the first reference. Caché saves this global name and subscript in the naked indicator, so that the subsequent naked global references can omit the global name “Produce” and the higher subscript level “fruit”. When the ^(3,1) naked reference goes to a lower subscript level, this new subscript level becomes the assumption for any subsequent naked global references.
   SET ^Produce("fruit",1)="Apples"  /* Full global reference  */
   SET ^(2)="Oranges"                /* Naked global references */
   SET ^(3)="Pears"                  /* assume subscript level 2 */
   SET ^(3,1)="Bartlett pears"       /* Go to subscript level 3  */
   SET ^(2)="Anjou pears"            /* Assume subscript level 3 */
   WRITE "latest global reference is: ",$ZREFERENCE,!
   ZWRITE ^Produce
   KILL ^Produce
 
This example sets the following global variables: ^Produce("fruit",1), ^Produce("fruit",2), ^Produce("fruit",3), ^Produce("fruit",3,1), and ^Produce("fruit",3,2).
With few exceptions, every global reference (full or naked) sets the naked indicator. The $ZREFERENCE special variable contains the full global name and subscripts of the most recent global reference, even if this was a naked global reference. The ZWRITE command also displays the full global name and subscripts of each global, whether or not it was set using a naked reference.
Naked global references should be used with caution, because Caché sets the naked indicator in situations that are not always obvious, including the following:
If a full global reference contains an extended global reference, subsequent naked global references assume the same extended global reference; you do not have to specify the extended reference as part of a naked global reference.