Chapter 3 Using Multidimensional Storage (Global Variables) (3)

Copy data in global variables

To copy the contents of a global variable (all or part of it) into another global variable (or local array), use the ObjectScript Merge command.

The following example demonstrates how to use the Merge command to copy the entire contents of the OldData global variable into the NewData global variable:

 Merge ^NewData = ^OldData
Copy the code

If the source parameter of the merge command has a subscript, all data in that node and its descendants is copied. If the Destination parameter has a subscript, the data is replicated using the Destination address as the top-level node. For example, the following code:

 Merge ^NewData(1.2) = ^OldData(5.6.7)
Copy the code

Copy ^OldData(5,6,7) and all data under it to ^NewData(1,2).

Maintain shared counters within global variables

A major concurrency bottleneck for large-scale transaction processing applications can be the creation of unique identifier values. For example, consider an order-processing application in which a unique identification number must be assigned to each new invoice. The traditional approach is to maintain some kind of counter table. Each process that creates a new invoice waits to acquire a lock on this counter, increments its value, and then unlocks it. This can lead to intense resource contention for this single record.

To solve this problem, InterSystems IRIS provides ObjectScript $INCREMENT functions. $INCREMENT Automatically increments the value of the global node (set to 1 if the node has no value). The atomicity of $INCREMENT means that no lock is required; This function is guaranteed to return a new increment without interference from any other process.

You can use $INCREMENT, as shown below. First, you must decide on the global node in which to store the counter. Next, whenever a new counter value is needed, just call $INCREMENT:

 SET counter = $INCREMENT(^MyCounter)
Copy the code

The default storage structure used by InterSystems IRIS objects and SQL uses $INCREMENT to allocate unique object (row) identifier values.

Sort the data in a global variable

Data stored in global variables is automatically sorted according to the value of the subscript. For example, the following ObjectScript code defines a set of global variables (in random order) and then iterates through them to demonstrate automatic ordering of global nodes by index:

/// w ##class(PHA.TEST.Global).GlobalSort()
ClassMethod GlobalSort(a)
{
	Kill ^Data

	Set ^Data("Cambridge") = ""
	Set ^Data("New York") = ""
	Set ^Data("Boston") = ""
	Set ^Data("London") = ""
	Set ^Data("Athens") = ""


	Set key = $Order(^Data(""))
	While (key '= "") { Write key,! Set key = $Order(^Data(key)) } q "" }Copy the code
DHC-APP> w ##class(PHA.TEST.Global).GlobalSort(a)Athens
Boston
Cambridge
London
New York
 
Copy the code

Applications can take advantage of automatic sorting provided by global functions to perform sort operations or maintain ordered, cross-referenced indexes of certain values. InterSystems SQL and ObjectScript automate these tasks using global variables.

Global variable node collation rules

The sort order of global variable nodes (called sorting) is controlled at two levels: inside the global variable itself and the application that uses it.

At the application level, you can control how global nodes are sorted by performing data transformations on values used as subscripts (InterSystems SQL and objects perform this through user-specified sorting functions). For example, if you create an alphabetical but case-insensitive list of names, you would normally use the uppercase version of the name as the subscript:

/// w ##class(PHA.TEST.Global).GlobalSortAlpha()
ClassMethod GlobalSortAlpha(a)
{
	Kill ^Data

	For name = "Cobra"."jackal"."zebra"."AARDVark" {
		Set ^Data($ZCONVERT(name,"U")) = name
	}


	Set key = $Order(^Data(""))
	While (key '= "") { Write ^Data(key),! Set key = $Order(^Data(key)) } q "" }Copy the code
DHC-APP>w ##class(PHA.TEST.Global).GlobalSortAlpha()
AARDVark
Cobra
jackal
zebra
 
Copy the code

This example converts each name to uppercase (using the $ZCONVERT function) so that subscripts are sorted regardless of case. Each node contains unconverted values so that the original values can be displayed.

Numeric and string value subscripts

Numeric values are sorted before string values; In other words, value1In the value"A"Before. You need to be aware of this if you use both numeric and string values for a given subscript. If global variables are used for indexing (that is, sorting data by value), it is most common to sort the values into numbers (such as salary)salaries) or a string (such as a zip codepostal codes).

For nodes sorted numerically, the typical solution is to use unary+The operator forces the subscript value to a numeric value. For example, if you want to build pairs by ageidValue to sort the index, then you can force the age to always be a number:

 Set ^Data(+age,id) = ""
Copy the code

If you want to sort values as strings (e.g"0022","0342","1584"), you can add a space ("") character to force the subscript value to always be a string. For example, if you are building a pair by zip codeidValue to sort the index, you can forcezipcodeAlways a string:

 Set ^Data(""_zipcode,id) = ""
Copy the code

This ensures that a value with a leading zero (such as “0022”) is always treated as a string.

$SORTBEGINand$SORTENDfunction

In general, you don’t have to worry about sorting data in InterSystems IRIS. Sorting is handled automatically, whether using SQL or direct global access.

In some cases, however, sorting can be done more efficiently. Specifically, when (1) a large number of random (i.e., unsorted) global nodes need to be set up, and (2) the total size of the generated global nodes approaches a large portion of the InterSystems IRIS buffer pool, Performance can be adversely affected – because many SET operations involve disk operations (because the data is not suitable for caching). This is usually the case when it involves creating indexed global functions, such as bulk data loading, index populating, or sorting unindexed values in temporary global functions

To handle these cases efficiently, ObjectScript provides $SORTBEGIN and $SORTEND functions. The $SORTBEGIN function initiates a special mode for the global variable (or part of it), in which the data set entered into the global variable is written to a special temporary 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 sequentially to the actual global store. The overall operation is more efficient because the actual writes are done in an order that requires fewer disk operations.

The $SORTBEGIN function is easy to use; Before starting the sort operation, call the global variable you want to sort with its name and call $SORTEND when the operation is complete:

/// w ##class(PHA.TEST.Global).GlobalSortBeginEnd()
ClassMethod GlobalSortBeginEnd(a)
{

	Kill ^Data
	
	// Initialize the sort mode globally for ^Data
	Set ret = $SortBegin(^Data)

	For i = 1:1:10000 {
		Set ^Data($Random(1000000=))""
	}

	Set ret = $SortEnd(^Data)

	// ^Data is now set and sorted

	Set start = $ZH 
	// Now iterate and display (in order)
	Set key = $Order(^Data(""))
	While (key '= "") { Write key,! Set key = $Order(^Data(key)) } Set elap = $ZH - start Write "Time (seconds): ",elap q "" }Copy the code

The $SORTBEGIN function is designed for special cases where global variables are created and must be used with care. In particular, in $SORTBEGIN mode, data cannot be read from global variables that are being written; Because the data is not written, the read will be incorrect.

InterSystems SQL automatically uses these functions to create temporary global indexes (such as sorting unindexed fields).

Use indirection in global variables

In an indirect way, ObjectScript provides a way to create global variable references at run time. This is useful for applications where the structure or name of a global variable is not known at the time the program is compiled.

The @ indirection operator supports indirection by removing a reference to a string containing an expression. Depending on how the @ operator is used, there are several types of indirection.

The following code provides an example of an indirect reference by name, in which a string containing a global reference is dereferenced using the @ operator:

/// w ##class(PHA.TEST.Global).GlobalIndirect()
ClassMethod GlobalIndirect(a)
{
	Kill ^Data
	Set var = "^Data(100)"
	// Now use indirect setting ^Data(100)
	Set @var = "This data was set indirectly."
	// Now display values directly:
	Write "Value: ",^Data(100)
	q ""
}
Copy the code
DHC-APP> w ##class(PHA.TEST.Global).GlobalIndirect(a)Value: This data was set indirectly.
Copy the code

It is also possible to mix expressions (variables or literal values) in indirect statements using subscript indirection:

/// w ##class(PHA.TEST.Global).GlobalIndirect1()
ClassMethod GlobalIndirect1(a)
{

	Kill ^Data
	Set glvn = "^Data"
	For i = 1:1:10 {
		Set @glvn@(i) = "This data was set indirectly."
	}

	Set key = $Order(^Data(""))
	While (key '= "") { Write "Value ",key, ": ", ^Data(key),! Set key = $Order(^Data(key)) } q "" }Copy the code
DHC-APP>w ##class(PHA.TEST.Global).GlobalIndirect1()
Value 1: This data was set indirectly.
Value 2: This data was set indirectly.
Value 3: This data was set indirectly.
Value 4: This data was set indirectly.
Value 5: This data was set indirectly.
Value 6: This data was set indirectly.
Value 7: This data was set indirectly.
Value 8: This data was set indirectly.
Value 9: This data was set indirectly.
Value 10: This data was set indirectly.
Copy the code

Indirection is a fundamental feature of ObjectScript; It is not limited to global references.