This section covers each of the XML Schema built-in datatypes and provides design issues, implementation considerations, and warnings when implementing an XML Schema datatype as a database column datatype. Both check constraints and triggers are listed, when appropriate, as both implementations are applicable in many cases.
The string datatype in XML Schema is defined to support, at a minimum, UTF-8 and UTF-16 representations. Oracle9i is the first version of the Oracle database to fully support these representations (Oracle8i has partial support for UTF-8). Oracle9i also supports 8-bit character sets such as ISO-Latin-8859-1 that might be appropriate if full international support is not required. Oraclea9i supports the following character column datatypes (each with a maximum length):
in which the “N” versions of the character column datatypes are used for UTF-8 and UTF-16 representations. Depending on the maximum length specified and whether the text is of a variable length, different character datatypes should be used.
A CLOB should not be used lightly, because a VARCHAR2 is more space- and time-efficient. However, CLOB support in Oracle9i has greatly increased over Oracle8i. The PL/SQL functions in Oracle8i that support VARCHAR2 now support a CLOB as well. Unfortunately, PL/SQL limits the size of character variables to 32,767 characters or fewer. Above this size, DBMS_LOB package functions must be used. Table 22.1 lists the mapping when DBMS_LOB package functions must be used.
Typical PL/SQL | DBMS_LOB Package Functions |
---|---|
INSTR | DBMS_LOB.INSTR |
LENGTH | DBMS_LOB.GET_LENGTH |
SUBSTR | DBMS_LOB.SUBSTR |
Implementations for the minLength constraining facet in this section cover both sets of functions.
Finally, the use of a before trigger for a CLOB is disallowed by Oracle because the :new value is not available. In addition, table check constraints are not allowed. As a result, using an after trigger forces application developers to roll back the insert or update statement manually.
The database CHAR or NCHAR column datatypes are appropriate if there is a length constraining facet. Unlike their variable equivalents, space for these database column datatypes is allocated on a fixed basis rather than on a variable basis.
There is no built-in database support for the length constraining facet, but this can be added as a column constraint in table creation or as a trigger. See Section 22.4.1.3 for details.
There is no built-in database support for the minLength constraining facet, but this can be added as a column constraint in table creation or as a trigger. Using the VARCHAR2 column datatype, Listing 22.7 implements this as a column constraint, and Listing 22.8 implements this as a trigger. Listing 22.9 implements the minLength, using a CLOB column datatype. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
Note
If the database table column is specified as an NCHAR, an NVARCHAR2, or an NCLOB, then use the LENGTHC function instead of the LENGTH function, as LENGTHC counts the number of octets in the value.
CREATE TABLE StringExample ( value VARCHAR2(30) CHECK (LENGTH(value) >= 5) ); |
CREATE TABLE StringExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertStringExample BEFORE INSERT ON StringExample FOR EACH ROW WHEN (LENGTH(new.value) < 5) BEGIN RAISE_APPLICATION_ERROR(-20000, 'String too small'), END; CREATE OR REPLACE TRIGGER UpdateStringExample BEFORE UPDATE ON StringExample FOR EACH ROW WHEN (LENGTH(new.value) < 5) BEGIN RAISE_APPLICATION_ERROR(-20000, 'String too small'), END; |
CREATE TABLE CLOBStringExample ( value CLOB ); CREATE OR REPLACE TRIGGER InsertCLOBStringExample AFTER INSERT ON CLOBStringExample FOR EACH ROW BEGIN IF DBMS_LOB.GETLENGTH(:new.value) < 5 THEN RAISE_APPLICATION_ERROR(-20000, 'String too small'), END IF; END; CREATE OR REPLACE TRIGGER UpdateCLOBStringExample BEFORE UPDATE ON CLOBStringExample FOR EACH ROW BEGIN IF DBMS_LOB.GETLENGTH(:new.value) < 5 THEN RAISE_APPLICATION_ERROR(-20000, 'String too small'), END IF; END; |
This facet is directly supported in the database because the maximum character length is specified at column creation. If the XML schema designer did not place a maxLength constraining facet, it is best to make the designer do so. If this is not possible, it is best to determine whether 4,000 characters/bytes are enough or whether a CLOB is required.
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
The normalizedString datatype is derived from string by restricting the allowable characters. To validate that a string is a normalizedString, a constraint or trigger may be created. The normalizedString datatype may not contain a carriage return (#xD), line feed (#xA), or tab (#x9). Listing 22.10 implements this datatype as a column constraint, and Listing 22.11 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE NormalizedStringExample ( value VARCHAR2(30) CHECK (INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0) ); |
CREATE TABLE NormalizedStringExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertNormalizedStringExample BEFORE INSERT ON NormalizedStringExample FOR EACH ROW WHEN (INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not normalizedString'), END; CREATE OR REPLACE TRIGGER UpdateNormalizedStringExample BEFORE UPDATE ON NormalizedStringExample FOR EACH ROW WHEN (INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not normalizedString'), END; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
The token datatype is derived from string by restricting the allowable characters. To validate that a string is a token, a constraint or trigger may be created. The token datatype may not contain a carriage return (#xD), line feed (#xA), tab (#x9), two space characters in a row, or spaces surrounding the token. Listing 22.12 implements this datatype as a column constraint, and Listing 22.13 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE TokenExample ( value VARCHAR2(30) CHECK (INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0) ); |
CREATE TABLE TokenExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertTokenExample BEFORE INSERT ON TokenExample FOR EACH ROW WHEN (INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not token'), END; CREATE OR REPLACE TRIGGER UpdateTokenExample BEFORE UPDATE ON TokenExample FOR EACH ROW WHEN (INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not token'), END; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
Restricting a database schema to valid language identifiers requires some work to set up and maintain. Refer to RFC 1766: Tags for the Identification of Languages 1995, http://www.ietf.org/rfc/rfc1766.txt for specific details. As a quick summary, a langcode may be any of the following:
A two-letter language code as defined by ISO 639.
A language identifier beginning with the prefix ‘i-’(or ‘I-’); these are registered with the Internet Assigned Numbers Authority (IANA).
A country code beginning with the prefixes as defined by ISO 3166.
A language identifier beginning with the prefix ‘x-’ (or ‘X-’); these are assigned for private use.
For more complete, accurate, and up-to-date information, review the RFC.
To partially ensure that valid language values are the only ones used, the values from ISO 639, the IANA, and ISO 3166 must be inserted into the database and checked for each entry. Because these are enumerations that will change over time, a picklist implementation is used. Because the constraints listed for languages are too complex for check constraints, triggers will be created instead.
Listing 22.14 adds the language code domains to the PicklistDomain table and some of the entries required to the Picklist table. Listing 22.15 creates a contrived table that uses the language domains with triggers to enforce the XML Recommendation, Extensible Markup Language (XML) 1.0 (Second Edition) on languages.
INSERT INTO PicklistDomain (picklistDomainID, name, description) VALUES (2, 'TwoLanguageLetterCodes', 'The two-letter language code as defined by [ISO 639],[ccc] Codes for the representation of names of languages'), INSERT INTO PicklistDomain (picklistDomainID, name, description) VALUES (3, 'IANACodes', 'A language identifier registered with the Internet Assigned [ccc] Numbers Authority [IANA]; these begin with the prefix i- (or I-)'), INSERT INTO PicklistDomain (picklistDomainID, name, description) VALUES (4, 'TwoLanguageCountryCodes', 'The country code from [ISO 3166], [ccc] Codes for the representation of names of countries.'), INSERT INTO Picklist (picklistID, picklistDomainID, name, description) VALUES (5, 2, 'en', 'English'), INSERT INTO Picklist (picklistID, picklistDomainID, name, description) VALUES (6, 3, 'I-', ''), INSERT INTO Picklist (picklistID, picklistDomainID, name, description) VALUES (7, 4, 'US', 'United States'), INSERT INTO Picklist (picklistID, picklistDomainID, name, description) VALUES (8, 4, 'GB', 'Great Britain'), COMMIT; |
CREATE TABLE LanguageExample ( value VARCHAR2(255) ); CREATE OR REPLACE TRIGGER InsertLanguageExample BEFORE INSERT ON LanguageExample FOR EACH ROW DECLARE I PLS_INTEGER; BEGIN I := 0; SELECT COUNT(1) INTO I FROM Picklist TwoLanguageLetterCodes, Picklist IANACodes, Picklist TwoLanguageCountryCodes WHERE --Match up the appropriate picklist domains to get --the right values. TwoLanguageLetterCodes.picklistDomainID = 2 AND IANACodes.picklistDomainID = 3 AND TwoLanguageCountryCodes.picklistDomainID = 4 AND (TwoLanguageLetterCodes.name = :new.value OR IANACodes.name = :new.value OR -- The language is private and starts with X- or x- INSTR(UPPER(:new.value), 'X-') = 1 OR -- The language is more than two characters and -- therefore must be followed by a country code -- subcode. (TwoLanguageLetterCodes.name = SUBSTR(:new.value, 1, 2) AND TwoLanguageCountryCodes.name = SUBSTR(:new.value, 4, 2))); IF I = 0 THEN RAISE_APPLICATION_ERROR(-20000, 'Not a language'), END IF; END; CREATE OR REPLACE TRIGGER UpdateLanguageExample BEFORE INSERT ON LanguageExample FOR EACH ROW WHEN (new.value <> old.value) DECLARE I PLS_INTEGER; BEGIN SELECT COUNT(1) INTO I FROM Picklist TwoLanguageLetterCodes, Picklist IANACodes, Picklist TwoLanguageCountryCodes WHERE --Match up the appropriate picklist domains to get --the right values. TwoLanguageLetterCodes.picklistDomainID = 2 AND IANACodes.picklistDomainID = 3 AND TwoLanguageCountryCodes.picklistDomainID = 4 AND (TwoLanguageLetterCodes.name = :new.value OR IANACodes.name = :new.value OR -- The language is private and starts with X- or x- INSTR(UPPER(:new.value), 'X-') = 1 OR -- The language is more than two characters and -- therefore must be followed by a country code -- subcode. (TwoLanguageLetterCodes.name = SUBSTR(:new.value, 1, 2) AND TwoLanguageCountryCodes.name = SUBSTR(:new.value, 4, 2))); IF I = 0 THEN RAISE_APPLICATION_ERROR(-20000, 'Not a language'), END IF; END; |
The two triggers could be refactored to call the same stored procedure.
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
The Name datatype is derived from token by restricting the first character to a letter or underscore or colon. Unfortunately, the allowable characters are over 250 ranges of Unicode characters that would be tedious to implement in Oracle9i.
An alternative is to determine the characters in the Name datatype required for your application, and use this set for compliance checking. If this set is small, enforcing this datatype is easier to implement. For example, if the character set is United States English, the list of allowable characters is ‘A’ to ‘Z’, ‘0’ to ‘9’, underscore, colon, period, and ASCII hyphen. Listing 22.16 implements this datatype as a column constraint, and Listing 22.17 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE NameExample ( value VARCHAR2(30) CHECK (LENGTH(TRANSLATE(UPPER(value), '_:.-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '_:.-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) = LENGTH(value) AND (UPPER(SUBSTR(value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(value, 1, 1) IN ('_', ':')) AND INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0) ); |
CREATE TABLE NameExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertNameExample BEFORE INSERT ON NameExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '_:.-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '_:.-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) IN ('_', ':')) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not Name'), END; CREATE OR REPLACE TRIGGER UpdateNameExample BEFORE UPDATE ON NameExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '_:.-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '_:.-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) IN ('_', ':')) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not Name'), END; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
The NCName datatype is derived from the Name datatype by restricting the first character to a letter or underscore. Unfortunately, the allowable characters are over 250 ranges of Unicode characters that would be tedious to implement in Oracle9i. NCName differs from the Name datatype in that the colon character is disallowed.
An alternative is to determine the characters in the NCName datatype required for your application, and use this set for compliance checking. If this set is small, enforcing this datatype is easier to implement. For example, if the character set is United States English, the list of allowable characters is ‘A’ to ‘Z’, ‘0’ to ‘9’, underscore, period, and ASCII hyphen. Listing 22.18 implements this datatype as a column constraint, and Listing 22.19 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE NCNameExample ( value VARCHAR2(30) CHECK (LENGTH(TRANSLATE(UPPER(value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) = LENGTH(value) AND (UPPER(SUBSTR(value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(value, 1, 1) = '_') AND INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0) ); |
CREATE TABLE NCNameExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertNCNameExample BEFORE INSERT ON NCNameExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) = '_') OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NCName'), END; CREATE OR REPLACE TRIGGER UpdateNCNameExample BEFORE UPDATE ON NCNameExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) = '_') OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NCName'), END; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
Although the ID datatype is derived from NCName, it has the same value space as NCName. Refer to Section 22.4.6 for details on how to implement this type.
Although the IDREF datatype is derived from NCName, it has the same value space as NCName. Refer to Section 22.4.6 for details on how to implement this type.
Because the IDREFS datatype is a list of IDREF datatypes, the same constraints that apply to IDREF (which essentially is an NCName) are applicable. Refer to Section 22.4.6 for details on how to implement this type. Unlike NCName, however, there are three distinct ways to implement the IDREFS datatype:
Create a single column to hold the list of IDREFS as a set of characters.
Create a table to hold the IDREFS that are associated with a primary table.
Create a single column of type VARRAY to hold the list of IDREFS.
Each of these approaches has its own merits. The first places little burden on the application developer or database designer and is applicable if the sole purpose is to insert, update, delete, and select the values in their entirety. The second requires more work from both the application developer and the database designer, but allows individual IDREF values that are part of the list to be easily inserted, updated, deleted, and selected. The third requires the most work from both the application developer and database designer and provides little benefit beyond the first approach. Its implementation will not be detailed.
In either the first or second approach, IDREFS is derived from the NCName datatype by restricting the first character to a letter or underscore. Unfortunately, the allowable characters are over 250 ranges of Unicode characters that would be tedious to implement in Oracle9i. An alternative is to determine the characters in the IDREFS datatype required for your application, and use this set for compliance checking. If this set is small, enforcing this datatype is easier to implement. For example, if the character set is United States English, the list of allowable characters is ‘A’ to ‘Z’, ‘0’ to ‘9’, space, period, ASCII hyphen, and underscore.
In a single column scenario, the IDREFS datatype is essentially a multivalued NCNAME datatype where the values are maintained as a single string. Listing 22.20 implements IDREFS datatype as a column constraint, and Listing 22.21 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE IDREFSExample ( value VARCHAR2(30) CHECK (LENGTH(TRANSLATE(UPPER(value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) = LENGTH(value) AND (UPPER(SUBSTR(value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(value, 1, 1) = '_') AND INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0) ); |
CREATE TABLE IDREFSExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertIDREFSExample BEFORE INSERT ON IDREFSExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) = '_') OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not IDREFS'), END; CREATE OR REPLACE TRIGGER UpdateIDREFSExample BEFORE UPDATE ON IDREFSExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) = '_') OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not IDREFS'), END; |
In a separate table scenario, the IDREFS datatype is essentially a multivalued NCNAME datatype where each value is stored separately in a table row. In Listing 22.22 and Listing 22.23, a sequence, primary keys, and a foreign key are created. A sequence provides the unique values for the primary key of the IDREFSExample table. The sole column of the IDREFSExample table, IDREFSExampleID, is a foreign key to the IDREFSInternalExample table that also has listOrder as the second part of the primary key. The IDREFSInternalExample table holds the IDREF value. Listing 22.24 shows how an IDREFS example of ‘a b c’ would be inserted into the database.
Listing 22.22 implements IDREFS datatype as a column constraint, and Listing 22.23 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
--Start the sequence at 1, don't let it cycle, and have --the numbers in the sequence ordered. CREATE SEQUENCE Seq_IDREFS INCREMENT BY 1 START WITH 1 MAXVALUE 999999999 MINVALUE 1 NOCYCLE ORDER; CREATE TABLE IDREFSExample ( IDREFSExampleID NUMBER(9) NOT NULL, PRIMARY KEY (IDREFSExampleID) ); CREATE TABLE IDREFSInternalExample ( IDREFSInternalExampleID NUMBER(9) NOT NULL, listOrder NUMBER(3) NOT NULL, value VARCHAR2(30) CHECK (LENGTH(TRANSLATE(UPPER(value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) = LENGTH(value) AND (UPPER(SUBSTR(value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(value, 1, 1) = '_') AND INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0), PRIMARY KEY (IDREFSInternalExampleID, listOrder), FOREIGN KEY (IDREFSInternalExampleID) REFERENCES IDREFSExample ); |
--Start the sequence at 1, don't let it cycle, and have --the numbers in the sequence ordered. CREATE SEQUENCE Seq_IDREFS INCREMENT BY 1 START WITH 1 MAXVALUE 999999999 MINVALUE 1 NOCYCLE ORDER; CREATE TABLE IDREFSExample ( IDREFSExampleID NUMBER(9) NOT NULL, PRIMARY KEY (IDREFSExampleID) ); CREATE TABLE IDREFSInternalExample ( IDREFSInternalExampleID NUMBER(9) NOT NULL, listOrder NUMBER(3) NOT NULL, value VARCHAR2(30), PRIMARY KEY (IDREFSInternalExampleID, listOrder), FOREIGN KEY (IDREFSInternalExampleID) REFERENCES IDREFSExample ); CREATE OR REPLACE TRIGGER InsertIDREFSInternalExample BEFORE INSERT ON IDREFSInternalExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) = '_') OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not IDREFS'), END; CREATE OR REPLACE TRIGGER UpdateIDREFSInternalExample BEFORE UPDATE ON IDREFSInternalExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=:', '.-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR NOT (UPPER(SUBSTR(new.value, 1, 1)) BETWEEN 'A' AND 'Z' OR SUBSTR(new.value, 1, 1) = '_') OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not IDREFS'), END; |
INSERT INTO IDREFSExample VALUES (Seq_IDREFS.NEXTVAL); INSERT INTO IDREFSInternalExample VALUES (Seq_IDREFS.CURRVAL, 1, 'a'), INSERT INTO IDREFSInternalExample VALUES (Seq_IDREFS.CURRVAL, 2, 'b'), INSERT INTO IDREFSInternalExample VALUES (Seq_IDREFS.CURRVAL, 3, 'c'), |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
Although the ENTITY datatype is derived from NCName, it has the same value space as NCName. Refer to Section 22.4.6 for details on how to implement this datatype.
Although the ENTITIES datatype is a not derived from IDREFS, it has the same value space as IDREFS. Refer to Section 22.4.9 for details on how to implement this datatype.
The NMTOKEN datatype is derived from the token datatype and restricts the allowable characters. Unfortunately, the allowable characters are over 250 ranges of Unicode characters that would be tedious to implement in Oracle9i.
An alternative is to determine the characters in the NMTOKEN datatype for required for your application, and use this set for compliance checking. If this set is small, enforcing this datatype is easier to implement. For example, if the character set is United States English, the list of allowable characters is ‘A’ to ‘Z’, ‘0’ to ‘9’, underscore, colon, period, and ASCII hyphen. Listing 22.25 implements NMTOKEN as a column constraint, and Listing 22.26 implements it as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE NMTOKENExample ( value VARCHAR2(30) CHECK (LENGTH(TRANSLATE(UPPER(value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) = LENGTH(value) AND INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0) ); |
CREATE TABLE NMTOKENExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertNMTOKENExample BEFORE INSERT ON NMTokenExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NMTOKEN'), END; CREATE OR REPLACE TRIGGER UpdateNMTOKENExample BEFORE UPDATE ON NMTokenExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NMTOKEN'), END; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
Because the NMTOKENS datatype is a list of NMTOKEN, the same constraints that apply to NMTOKEN are applicable. Refer to Section 22.4.12 for details on how to implement this datatype. Unlike NMTOKEN, however, there are three distinct ways to implement this datatype:
Create a single column to hold the list of NMTOKENS as a set of characters.
Create a table to hold the NMTOKENS that are associated with a primary table.
Create a single column of type VARRAY to hold the list of NMTOKENS.
Each of these approaches has its own merits. The first places little burden on the application developer or database designer and is applicable if the sole purpose is to insert, update, delete, and select the values in their entirety. The second requires more work from both the application developer and database designer, but allows individual NMTOKEN values that are part of the list to be easily inserted, updated, deleted, and selected. The third requires the most work from both the application developer and database designer and provides little benefit beyond the first approach. Its implementation will not be detailed.
In either the first or second approach, NMTOKENS is derived from the NMTOKEN datatype by restricting the first character to a letter or underscore. Unfortunately, the allowable characters are over 250 ranges of Unicode characters that would be tedious to implement in Oracle9i. An alternative is to determine the characters in the NMTOKENS datatype required for your application, and use this set for compliance checking. If this set is small, enforcing this datatype is easier to implement. For example, if the character set is United States English, the list of allowable characters is ‘A’ to ‘Z’, ‘0’ to ‘9’, space, period, ASCII hyphen, underscore, and colon.
In a single column scenario, the NMTOKENS datatype is essentially a multivalued NMTOKEN datatype where the values are maintained as a single string. Listing 22.27 implements this datatype as a column constraint, and Listing 22.28 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE NMTOKENSExample ( value VARCHAR2(30) CHECK (LENGTH(TRANSLATE(UPPER(value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) = LENGTH(value) AND INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0) ); |
CREATE TABLE NMTOKENSExample ( value VARCHAR2(30) ); CREATE OR REPLACE TRIGGER InsertNMTOKENSExample BEFORE INSERT ON NMTOKENSExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NMTOKENS'), END; CREATE OR REPLACE TRIGGER UpdateNMTOKENSExample BEFORE UPDATE ON NMTOKENSExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NMTOKENS'), END; |
In a separate table scenario, the NMTOKENS datatype is essentially a multivalued NMTOKEN datatype where each value is stored separately in a table row. In Listing 22.29 and Listing 22.30, a sequence, primary keys, and a foreign key are created. A sequence provides the unique values for the primary key of the NMTOKENSExample table. The sole column of the NMTOKENSExample table, NMTOKENSExampleID, is a foreign key to the NMTOKENSInternalExample table that also has listOrder as the second part of the primary key. The NMTOKENSInternalExample table holds the NMTOKEN value. Listing 22.31 shows how a NMTOKENS example of ‘a b c’ would be inserted into the database.
Listing 22.29 implements this datatype as a column constraint, and Listing 22.30 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
--Start the sequence at 1, don't let it cycle, and have --the numbers in the sequence ordered. CREATE SEQUENCE Seq_NMTOKENS INCREMENT BY 1 START WITH 1 MAXVALUE 999999999 MINVALUE 1 NOCYCLE ORDER; CREATE TABLE NMTOKENSExample ( NMTOKENSExampleID NUMBER(9) NOT NULL, PRIMARY KEY (NMTOKENSExampleID) ); CREATE TABLE NMTOKENSInternalExample ( NMTOKENSInternalExampleID NUMBER(9) NOT NULL, listOrder NUMBER(3) NOT NULL, value VARCHAR2(30) CHECK (LENGTH(TRANSLATE(UPPER(value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) = LENGTH(value) AND INSTR(value, UNISTR(' 00D')) = 0 AND INSTR(value, UNISTR(' 00A')) = 0 AND INSTR(value, UNISTR(' 009')) = 0 AND LENGTH(value) = LENGTH(TRIM(value)) AND INSTR(value, ' ') = 0), PRIMARY KEY (NMTOKENSInternalExampleID, listOrder), FOREIGN KEY (NMTOKENSInternalExampleID) REFERENCES NMTOKENSExample ); |
--Start the sequence at 1, don't let it cycle, and have --the numbers in the sequence ordered. CREATE SEQUENCE Seq_NMTOKENS INCREMENT BY 1 START WITH 1 MAXVALUE 999999999 MINVALUE 1 NOCYCLE ORDER; CREATE TABLE NMTOKENSExample ( NMTOKENSExampleID NUMBER(9) NOT NULL, PRIMARY KEY (NMTOKENSExampleID) ); CREATE TABLE NMTOKENSInternalExample ( NMTOKENSInternalExampleID NUMBER(9) NOT NULL, listOrder NUMBER(3) NOT NULL, value VARCHAR2(30), PRIMARY KEY (NMTOKENSInternalExampleID, listOrder), FOREIGN KEY (NMTOKENSInternalExampleID) REFERENCES NMTOKENSExample ); CREATE OR REPLACE TRIGGER InsertNMTOKENSInternalExample BEFORE INSERT ON NMTOKENSInternalExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NMTOKENS'), END; CREATE OR REPLACE TRIGGER UpdateNMTOKENSInternalExample BEFORE UPDATE ON NMTOKENSInternalExample FOR EACH ROW WHEN (LENGTH(TRANSLATE(UPPER(new.value), '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/;''[]<>?"{}|~!@#$%^&*()+=', '.-_:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')) <> LENGTH(new.value) OR INSTR(new.value, UNISTR(' 00D')) <> 0 OR INSTR(new.value, UNISTR(' 00A')) <> 0 OR INSTR(new.value, UNISTR(' 009')) <> 0 OR LENGTH(new.value) <> LENGTH(TRIM(new.value)) OR INSTR(new.value, ' ') <> 0) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not NMTOKENS'), END; |
INSERT INTO NMTOKENSExample VALUES (Seq_NMTOKENS.NEXTVAL); INSERT INTO NMTOKENSInternalExample VALUES (Seq_NMTOKENS.CURRVAL, 1, 'a'), INSERT INTO NMTOKENSInternalExample VALUES (Seq_NMTOKENS.CURRVAL, 2, 'b'), INSERT INTO NMTOKENSInternalExample VALUES (Seq_NMTOKENS.CURRVAL, 3, 'c'), |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
The decimal datatype can be represented as an Oracle NUMBER datatype. Oracle guarantees portability of numbers that have a precision of up to 38 total digits. Numbers that have more digits should be avoided, if possible. Also note that XML schema processors are only required to support 18 total digits.
Warning
XML schema validators might round a decimal value with greater than 18 total digits, or generate an error, or do anything else as this portion of the Schema Recommendation is not clear. The database designer should determine—with the XML schema designer—whether this precise level of validation needs to occur, and if so, which choice is appropriate. This issue will most likely be resolved in an XML Schema errata document.
Database designers should avoid having an XML schema document with decimal-derived datatypes that have neither totalDigits nor min/max exclusive/inclusive constraining facets defined. These might already be implicit if the development language is Java, Visual Basic, and so on, because language datatypes have an upper and lower bound. If the constraining facets are not explicitly or implicitly defined, determining the intention of the XML schema designer avoids having potential decimal values dependent on the instance of the XML schema processor, incredibly large or small numbers, and so on.
Listing 22.32 creates the TotalDigitsExample table with a value column with 18 total digits.
CREATE TABLE TotalDigitsExample ( value NUMBER(18) ); |
Listing 22.33 creates the FractionDigitsExample table with a value column having 18 total digits and 2 fractional digits.
CREATE TABLE FractionDigitsExample ( value NUMBER(18, 2) ); |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but this can be added as a column constraint in table creation or as a trigger. Listing 22.34 implements the maxInclusive constraining facet in a column constraint, and Listing 22.35 implements it as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE MaxInclusiveExample ( value NUMBER(3, 2) CHECK (value <= 5) ); |
CREATE TABLE MaxInclusiveExample ( value NUMBER(3, 2) ); CREATE OR REPLACE TRIGGER InsertMaxInclusiveExample BEFORE INSERT ON MaxInclusiveExample FOR EACH ROW WHEN (new.value > 5) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Number too large'), END; CREATE OR REPLACE TRIGGER UpdateMaxInclusiveExample BEFORE UPDATE ON MaxInclusiveExample FOR EACH ROW WHEN (new.value > 5) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Number too large'), END; |
There is no built-in database support for the maxExclusive constraining facet, but this can be added as a column constraint in table creation or as a trigger. Listing 22.36 implements the maxExclusive constraining facet in a column constraint, and Listing 22.37 implements it as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE MaxExclusiveExample ( value NUMBER(3, 2) CHECK (value < 5) ); |
CREATE TABLE MaxExclusiveExample ( value NUMBER(3, 2) ); CREATE OR REPLACE TRIGGER InsertMaxExclusiveExample BEFORE INSERT ON MaxExclusiveExample FOR EACH ROW WHEN (new.value > 4) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Number too large'), END; CREATE OR REPLACE TRIGGER UpdateMaxExclusiveExample BEFORE UPDATE ON MaxExclusiveExample FOR EACH ROW WHEN (new.value > 4) BEGIN RAISE_APPLICATION_ERROR(-20000, ' Number too large'), END; |
There is no built-in database support for the minInclusive constraining facet, but this can be added as a column constraint in table creation or as a trigger. Listing 22.38 implements the minInclusive constraining facet in a column constraint, and Listing 22.39 implements it as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE MinInclusiveExample ( value NUMBER(18, 2) CHECK (value >= 5) ); |
CREATE TABLE MinInclusiveExample ( value NUMBER(18, 2) ); CREATE OR REPLACE TRIGGER InsertMinInclusiveExample BEFORE INSERT ON MinInclusiveExample FOR EACH ROW WHEN (new.value > 5) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Number too small'), END; CREATE OR REPLACE TRIGGER UpdateMinInclusiveExample BEFORE UPDATE ON MinInclusiveExample FOR EACH ROW WHEN (new.value > 5) BEGIN RAISE_APPLICATION_ERROR(-20000, ' Number too small'), END; |
There is no built-in database support for the minExclusive constraining facet, but this can be added as a column constraint in table creation or as a trigger. Listing 22.40 implements the minExclusive constraining facet in a column constraint, and Listing 22.41 implements it as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE MinExclusiveExample ( value NUMBER(18, 2) CHECK (value < 5) ); |
CREATE TABLE MinExclusiveExample ( value NUMBER(18, 2) ); CREATE OR REPLACE TRIGGER InsertMinExclusiveExample BEFORE INSERT ON MinExclusiveExample FOR EACH ROW WHEN (new.value > 4) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Number too small'), END; CREATE OR REPLACE TRIGGER UpdateMinExclusiveExample BEFORE UPDATE ON MinExclusiveExample FOR EACH ROW WHEN (new.value > 4) BEGIN RAISE_APPLICATION_ERROR(-20000, ' Number too small'), END; |
The integer datatype is derived from the decimal datatype by restricting the values to have 0 fractionDigits. Refer to Section 22.4.14 for design and implementation issues of this datatype.
The nonPositiveInteger datatype is derived by restriction from the integer datatype with a maxInclusive of 0. Refer to Section 22.4.14 for design and implementation issues of this type.
The negativeInteger datatype is derived by restriction from the nonPositiveInteger number with a maxInclusive of −1. Refer to Section 22.4.14 for design and implementation issues of this type.
The long datatype is derived by restriction from the integer datatype with a maxInclusive of 9223372036854775807 and a minInclusive of −9223372036854775808. Refer to Section 22.4.14 for design and implementation issues of this type.
The int datatype is derived by restriction from the long datatype with a maxInclusive of 2147483647 and a minInclusive of −2147483648. Refer to Section 22.4.14 for design and implementation issues of this type.
The short datatype is derived by restriction from the int datatype with a maxInclusive of 32767 and a minInclusive of −32768. Refer to Section 22.4.14 for design and implementation issues of this type.
The byte datatype is derived by restriction from the short datatype with a maxInclusive of 127 and a minInclusive of −128. Refer to Section 22.4.14 for design and implementation issues of this type.
The nonNegativeInteger datatype is derived by restriction from the integer datatype with a minInclusive of 0. Refer to Section 22.4.14 for design and implementation issues of this type.
The unsignedLong datatype is derived by restriction from the nonNegativeInteger datatype with a maxInclusive of 18446744073709551615 and a minInclusive of 0. Refer to Section 22.4.14 for design and implementation issues of this type.
The unsignedInt datatype is derived by restriction from the unsignedLong number with a maxInclusive of 4294967295 and a minInclusive of 0. Refer to Section 22.4.14 for design and implementation issues of this type.
The unsignedShort datatype is derived by restriction from the unsignedInt datatype with a maxInclusive of 65535 and a minInclusive of 0. Refer to Section 22.4.14 for design and implementation issues of this type.
The unsignedByte datatype is derived by restriction from the unsignedShort datatype with a maxInclusive of 255 and a minInclusive of 0. Refer to Section 22.4.14 for design and implementation issues of this type.
The positiveInteger datatype is derived by restriction from the integer datatype with a minInclusive of 1. Refer to Section 22.4.14 for design and implementation issues of this type.
Oracle does not support the IEEE 754-1985 specification for numbers on which this datatype is based. The values NOT-A-NUMBER, POSITIVE INFINITY, and NEGATIVE INFINITY simply cannot be represented directly. Refer to Section 22.4.14 on approximating the implementation of the float datatype. This representation accurately represent floats for most business applications.
If a complete representation is required, a number of solutions are possible:
Use one of the character column datatypes instead—a good solution when the data only needs to be stored and retrieved.
Use a set of numbers outside the min/max inclusion/exclusion range to signify each of the possible unrepresentable values—a good solution when the floats need to be manipulated inside the database, but requires implementation of IEEE 754-1985 rules for addition of NOT-A-NUMBER and POSITIVE INFINITY, and so on.
Create database columns for each of the possible unrepresentable values—a good solution when the floats need to be manipulated inside the database and the IEEE 754-1985 value needs to be made explicit (by creating a separate column), but requires implementation of IEEE 754-1985 rules for addition of NOT-A-NUMBER and POSITIVE INFINITY, and so on.
For each of these solutions, you can create Java stored procedures that take floats or floats represented by characters and perform the arithmetic operations in Java. The Java stored procedure then returns an appropriate float or an appropriate float represented by characters.
Tip
Java did not support the IEEE 754-1985 standard for floats until the introduction of the ‘strict’ keyword. To get full IEEE 754-1985 support, this keyword needs to be used when creating a class.
Oracle does not support the IEEE 754-1985 specification for numbers on which this datatype is based. The values NOT-A-NUMBER, POSITIVE INFINITY, and NEGATIVE INFINITY simply cannot be represented directly. Refer to Section 22.4.14 on approximating the implementation of the double datatype. This representation accurately represent floats for most business applications.
If a complete representation is required, refer to Section 22.4.28 for a series of options.
The hexBinary datatype can be represented naturally in one of three ways: as a VARCHAR2, CLOB, or BLOB. When the minLength or length has been specified at fewer than 4,000 characters, VARCHAR2 has significant performance and ease-of-use advantages over a CLOB or BLOB from an application development perspective. At over 4,000 characters, a CLOB or BLOB must be used. A BLOB is much more compact; constraining facets are more difficult to implement. A CLOB is space inefficient, but allows a straightforward implementation of the constraining facets.
A BLOB is not recommended when a constraining facet occurs. Unfortunately, a BLOB value must be decoded to check the constraint in after-insert and after-update triggers. A before trigger is not allowed. The result is slow performance and the requirement for the application to manually rollback any errors. When there are no constraining facets, a BLOB is a good implementation. To convert a hexBinary value to a BLOB, call the UTL_ENCODE.UUENCODE PL/SQL procedure. To convert a BLOB to a hexBinary value, call the UTL_ENCODE.UUDECODE PL/SQL procedure.
Refer to Section 22.4.1.2 for design and implementation issues.
Refer to Section 22.4.1.3 for design and implementation issues.
Refer to Section 22.4.1.4 for design and implementation issues.
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Although base64Binary is not derived by restriction from the hexBinary datatype, it has almost identical design issues and implementation details. Review Section 22.4.30 for design and implementation issues of this datatype. Note that a base64Binary value can be converted to a BLOB by using the UTL_ENCODE.BASE64_UUENCODE procedure. To convert a BLOB to a base64Binary value, call the UTL_ENCODE.BASE64_UUDECODE procedure.
The QName datatype is a combination of the anyURI datatype and the NCName datatype. QName is probably best represented as two columns in a table. Refer to Sections 22.4.33 and 22.4.6 for design and implementation issues of this type.
The anyURI datatype can now be implemented directly in Oracle9i. The PL/SQL SYS package includes a UriType type and a URI factory class. However, from an XML schema perspective, there is little to recommend the use of the SYS.UriType over the VARCHAR2 datatype, because Oracle performs little or no syntax checking of the URI. In addition, many of the older—but still widely used—database drivers do not support Oracle objects. The anyURI datatype is probably best treated as a string datatype, because it has the same constraining facets. Refer to Section 22.4.1 for information on designing and implementing the anyURI datatype.
Using the SYS.UriType column datatype in Oracle has potential benefits for an application. Oracle supports functionality with respect to URLs and internal database URIs that can aid in application development as the location of data within the database can now be specified with a URI.
Although NOTATION datatype is not derived by restriction from the QName datatype, it has the same value space. Refer to Section 22.4.32 for design and implementation issues of this type.
Oracle does not yet support the boolean datatype as defined in the SQL-92 standard. As a result, a boolean datatype should be mapped to either a database NUMBER or VARCHAR column datatype.
The boolean datatype has the following legal literals: ‘true’, ‘false’, ‘1’, and ‘0’. Having all four literals in the database is problematic because there are only two values in the value space. The database designer should communicate with the XML schema designer to determine if all four literals will exist in XML instances. If all four literals are possible, then consider using a database NUMBER column datatype to store the value and using a DECODE statement to map ‘true’ to 1 and ‘false’ to 0. The following SQL fragment can accomplish this:
DECODE(?booleanValue, 'true', 1, 'false', 0, 1, 1, 0, 0)
where the ?booleanValue bind variable is replaced by the appropriate Boolean value.
In this scenario, the boolean datatype is represented by the strings of ‘true’ and ‘false’. Listing 22.42 implements this as a column constraint, and Listing 22.43 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE BooleanStringExample ( value VARCHAR(5) CHECK (value = 'true' OR value = 'false') ); |
CREATE TABLE BooleanStringExample ( value VARCHAR(5) ); CREATE OR REPLACE TRIGGER InsertBooleanStringExample BEFORE INSERT ON BooleanStringExample FOR EACH ROW WHEN (new.value <> 'true' AND new.value <> 'false') BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not a boolean'), END; CREATE OR REPLACE TRIGGER UpdateBooleanStringExample BEFORE UPDATE ON BooleanStringExample FOR EACH ROW WHEN (new.value <> 'true' AND new.value <> 'false') BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not a boolean'), END; |
In this scenario, the boolean datatype is represented by the numbers 0 and 1. Listing 22.44 implements this as a column constraint, and Listing 22.45 implements this as a trigger. Refer to Section 22.3 regarding the decision to use column constraints or triggers.
CREATE TABLE BooleanNumberExample ( value NUMBER(1) CHECK (value IN (0, 1)) ); |
CREATE TABLE BooleanNumberExample ( value NUMBER(1) ); CREATE OR REPLACE TRIGGER InsertBooleanNumberExample BEFORE INSERT ON BooleanNumberExample FOR EACH ROW WHEN (new.value NOT IN (0, 1)) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not boolean'), END; CREATE OR REPLACE TRIGGER UpdateBooleanNumberExample BEFORE UPDATE ON BooleanNumberExample FOR EACH ROW WHEN (new.value NOT IN (0, 1)) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Not boolean'), END; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
The duration datatype represents a duration of time that is either positive or negative and may include years, months, hours, minutes, seconds, and fractional seconds. The duration value space is partially ordered. For example, one year is greater than 364 days, not comparable with 365 days, not comparable with 366 days, and less than 367 days (because some years are equal to 365 days and others are equal to 366 days).
Validating a duration requires pattern matching through the Java JVM in Oracle or a significant effort in hand-coded PL/SQL. Validating the minimum and maximum constraining facets requires a significant amount of hand-coded PL/SQL, again because duration values are only partially ordered.
If the database designer can determine that the XML instances will be constrained to one duration components (‘Y’, ‘M’, ‘D’, and so on), then the durations are totally ordered and can be represented as a decimal datatype or integer datatype. Refer to Sections 22.4.14 and 22.4.15 for design and implementation issues of the duration subtypes.
The duration datatype is probably best treated as a string datatype, with validation left to the application developer. If validation is required by the database, refer to Section 22.2.1.
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
Refer to Section 22.4.14.6 for design and implementation issues.
Refer to Section 22.4.14.7 for design and implementation issues.
Refer to Section 22.4.14.8 for design and implementation issues.
Refer to Section 22.4.14.9 for design and implementation issues.
The dateTime datatype represents a specific instance in time. Oracle9i now has a TIMESTAMP column datatype that represents a date, time of day, and an optional time zone. A dateTime value may or may not include a time zone. The database designer should confer with the XML schema designer to determine if time-zone values will exist in XML instances and, if so, whether the values need to be preserved or the dateTime values can be reduced to their canonical form. This section on the dateTime datatype assumes that they exist in the XML instance and must be preserved.
Warning
Mixing datetime values that have time zones with those that do not in the same database table column is problematic. From an XML schema perspective, the values are only partially ordered since the time zone is not specified in some cases. However, when using a database TIMESTAMP WITH TIME ZONE column datatype, Oracle9i will default the time zone to the time zone of the database server that results in a complete ordering. Separating datetime values that have time zones with those that do not into two database columns is advisable.
Listing 22.46 implements a dateTime table creation.
CREATE TABLE DateTimeExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a dateTime datatype, Oracle needs to convert a value from its ISO 8601 standard format to an internal Oracle representation.
Warning
The database TIMESTAMP column datatype does not support the time value of ‘24:00:00’, but does support its value space equivalent ‘00:00:00’.
Listing 22.47 is a SQL statement that inserts a dateTime into the DateTimeExample table (?datetimeValue is the dateTime bind variable).
INSERT INTO DateTimeExample (value) SELECT TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM') FROM dual; |
Listing 22.48 is a SQL statement that retrieves all of the values from the DateTimeExample table.
SELECT TO_CHAR(value, 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM') FROM DateTimeExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.49 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE DatetimeMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDatetimeMaxInclusive BEFORE INSERT ON DatetimeMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too late'), END; CREATE OR REPLACE TRIGGER UpdateDatetimeMaxInclusive BEFORE UPDATE ON DatetimeMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.50 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE DatetimeMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDatetimeMaxExclusive BEFORE INSERT ON DatetimeMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too late'), END; CREATE OR REPLACE TRIGGER UpdateDatetimeMaxExclusive BEFORE UPDATE ON DatetimeMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.51 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE DatetimeMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDatetimeMinInclusive BEFORE INSERT ON DatetimeMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too early'), END; CREATE OR REPLACE TRIGGER UpdateDatetimeMinInclusive BEFORE UPDATE ON DatetimeMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.52 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE DatetimeMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDatetimeMinExclusive BEFORE INSERT ON DatetimeMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too early'), END; CREATE OR REPLACE TRIGGER UpdateDatetimeMinExclusive BEFORE UPDATE ON DatetimeMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('2001-12-21T12:45:30Z+05:00', 'YYYY-MM-DD"T"HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Datetime too early'), END; |
The date datatype represents a Gregorian calendar date. Oracle has a DATE column datatype that represents a date and time without a time-zone offset and a TIMESTAMP column datatype that represents both date and time with a time-zone offset. The database designer should confer with the XML schema designer to determine if time-zone values will exist in the XML instances because implementations using the DATE column datatype are simpler. This section on the date datatype assumes that time zones exist in the XML instances and must be preserved.
Warning
Mixing date values that have time zones with those that do not in the same database table column is problematic. From an XML schema perspective, the values are only partially ordered since the time zone is not specified in some cases. However, when using a database TIMESTAMP WITH TIME ZONE column datatype, Oracle9i will default the time zone to the time zone of the database server that results in a complete ordering. Separating date values that have time zones and those that do not into two database columns is advisable.
Listing 22.53 implements a date table creation.
CREATE TABLE DateExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a date datatype, Oracle needs to convert it from its ISO 8601 standard format to an internal Oracle representation. Listing 22.54 is a SQL statement that inserts a date into the DateExample table (?dateValue is the date bind variable).
INSERT INTO DateExample (value) SELECT TO_TIMESTAMP_TZ(?dateValue, 'YYYY-MM-DD"Z"TZH:TZM') FROM dual; |
Listing 22.55 is a SQL statement that retrieves all of the values from the DateExample table.
SELECT TO_CHAR(value, 'YYYY-MM-DD"Z"TZH:TZM') FROM DateExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.56 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE DateMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDateMaxInclusiveExample BEFORE INSERT ON DateMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too late'), END; CREATE OR REPLACE TRIGGER UpdateDateMaxInclusiveExample BEFORE UPDATE ON DateMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.57 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE DateMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDateMaxExclusiveExample BEFORE INSERT ON DateMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too late'), END; CREATE OR REPLACE TRIGGER UpdateDateMaxExclusiveExample BEFORE UPDATE ON DateMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.58 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE DateMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDateMinInclusiveExample BEFORE INSERT ON DateMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too early'), END; CREATE OR REPLACE TRIGGER UpdateDateMinInclusiveExample BEFORE UPDATE ON DateMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.59 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE DateMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertDateMinExclusiveExample BEFORE INSERT ON DateMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too early'), END; CREATE OR REPLACE TRIGGER UpdateDateMinExclusiveExample BEFORE UPDATE ON DateMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('2001-12-24Z05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too early'), END; |
The gYear datatype represents a Gregorian calendar year. Without the optional time zone, gYear is essentially an integer datatype. Refer to Sections 22.4.14 and 22.4.15 for details implementing gYear when no time-zone offsets are required.
If time-zone offsets are required, the representation becomes much more complex. Oracle9i now has a TIMESTAMP column datatype that represents both date and time with a time-zone offset. The database designer should confer with the XML schema designer to determine if time-zone values will exist in XML instances and, if so, whether the values need to be preserved or the gYear values can be reduced to their canonical form.
Warning
Mixing gYear values that have time zones with those that do not in the same database table column is problematic. From an XML schema perspective, the values are only partially ordered since the time zone is not specified in some cases. However, when using a database TIMESTAMP WITH TIME ZONE column datatype, Oracle9i will default the time zone to the time zone of the database server that results in a complete ordering. Separating gYear values that have time zones and those that do not into two database columns is advisable.
Listing 22.60 implements a gYear table creation.
CREATE TABLE GYearExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a gYear datatype, Oracle needs to convert the datatype from its XML Schema format to an internal Oracle representation. Listing 22.59 is a SQL statement that inserts a gYear into the GYearExample table (?gYearValue is the gYear bind variable). Note that January 1st was arbitrarily chosen as the day of the year.
INSERT INTO gYearExample (value) SELECT TO_TIMESTAMP_TZ(?gYearValue || '01-01', 'YYYY"Z"TZH:TZM MM-DD') FROM dual; |
Listing 22.62 is a SQL statement that retrieves all of the values from the GYearExample table.
SELECT TO_CHAR(value, 'YYYY"Z"TZH:TZM') FROM GYearExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.63 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMaxInclusive BEFORE INSERT ON GYearMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYear too late'), END; CREATE OR REPLACE TRIGGER UpdateGYearMaxInclusive BEFORE UPDATE ON GYearMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.64 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMaxExclusive BEFORE INSERT ON GYearMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYear too late'), END; CREATE OR REPLACE TRIGGER UpdateGYearMaxExclusive BEFORE UPDATE ON GYearMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYear too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.65 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMinInclusive BEFORE INSERT ON GYearMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYear too early'), END; CREATE OR REPLACE TRIGGER UpdateGYearMinInclusive BEFORE UPDATE ON GYearMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYear too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.66 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMinExclusive BEFORE INSERT ON GYearMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYear too early'), END; CREATE OR REPLACE TRIGGER UpdateGYearMinExclusive BEFORE UPDATE ON GYearMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('2001-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYear too early'), END; |
The gYearMonth datatype represents a Gregorian calendar month. Oracle9i now has a TIMESTAMP column datatype that represents both date and time with a time-zone offset. The database designer should confer with the XML schema designer to determine if time-zone values will exist in XML instances and, if so, whether the values need to be preserved or the gYearMonth values can be reduced to their canonical form. Listing 22.67 implements a gYearMonth table creation.
CREATE TABLE GYearMonthExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a gYearMonth datatype, Oracle needs to convert it from its XML Schema format to an internal Oracle representation. Listing 22.68 is a SQL statement that inserts a gYearMonth into the GYearMonthExample table (?gYearMonthValue is the gYearMonth bind variable). Note that the 1st was arbitrarily chosen as the day of the month.
INSERT INTO gYearMonthExample (value) SELECT TO_TIMESTAMP_TZ(?gYearMonthValue || '01', 'YYYY-MM"Z"TZH:TZM DD') FROM dual; |
Listing 22.69 is a SQL statement that retrieves all of the values from the GYearMonthExample table.
SELECT TO_CHAR(value, 'YYYY-MM"Z"TZH:TZM') FROM GYearMonthExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.70 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMonthMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMonthMaxInclusive BEFORE INSERT ON GYearMonthMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too late'), END; CREATE OR REPLACE TRIGGER UpdateGYearMonthMaxInclusive BEFORE UPDATE ON GYearMonthMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.71 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMonthMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMonthMaxExclusive BEFORE INSERT ON GYearMonthMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too late'), END; CREATE OR REPLACE TRIGGER UpdateGYearMonthMaxExclusive BEFORE UPDATE ON GYearMonthMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.72 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMonthMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMonthMinInclusive BEFORE INSERT ON GYearMonthMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too early'), END; CREATE OR REPLACE TRIGGER UpdateGYearMonthMinInclusive BEFORE UPDATE ON GYearMonthMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.73 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions. This could be obviated in this case by not using the ISO 8601 dateTime format and using the default Oracle external date format, but this is not shown here.
CREATE TABLE GYearMonthMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGYearMonthMinExclusive BEFORE INSERT ON GYearMonthMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too early'), END; CREATE OR REPLACE TRIGGER UpdateGYearMonthMinExclusive BEFORE UPDATE ON GYearMonthMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('1999-01-01Z+05:00', 'YYYY-MM-DD"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GYearMonth too early'), END; |
The time datatype represents an instance of time that recurs every day. A time value may or may not include a time zone. Oracle9i now has a TIMESTAMP column datatype that represents a date, time of day, and an optional time zone. The database designer should confer with the XML schema designer to determine if time-zone values will exist in XML instances and, if so, whether the values need to be preserved or the time values can be reduced to their canonical form. Listing 22.74 implements a time table creation.
CREATE TABLE TimeExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a time datatype, Oracle needs to convert it from its XML Schema format to an internal Oracle representation. In addition, because Oracle needs a date for the insert, it is appropriate to use a date that indicates that no date was specified. Oracle has a minimum date of January 1, 4712 B.C. It is represented by the number 1 and is translated into January 1, 4712 B.C. by the ‘J’ datetime format element. A hard-coded date is especially useful when min/max inclusion/exclusion facets have been specified.
Warning
The database TIMESTAMP column datatype does not support the time value of ‘24:00:00’, but does support its value space equivalent ‘00:00:00’.
Listing 22.75 is a SQL statement that inserts a time into the TimeExample table (?timeValue is the time bind variable).
INSERT INTO TimeExample (value) SELECT TO_TIMESTAMP_TZ('1 ' || ?timeValue, 'J HH24:MI:SS"Z"TZH:TZM') FROM dual; |
Listing 22.76 is a SQL statement that retrieves all of the values from the TimeExample table.
SELECT TO_CHAR(value, 'HH24:MI:SS"Z"TZH:TZM') FROM TimeExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.77 shows how this can be enforced with a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE TimeMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertTimeMaxInclusiveExample BEFORE INSERT ON TimeMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too late'), END; CREATE OR REPLACE TRIGGER UpdateTimeMaxInclusiveExample BEFORE UPDATE ON TimeMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.78 shows how this can be enforced with a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE TimeMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertTimeMaxExclusiveExample BEFORE INSERT ON TimeMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too late'), END; CREATE OR REPLACE TRIGGER UpdateTimeMaxExclusiveExample BEFORE UPDATE ON TimeMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.79 shows how this can be enforced with a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE TimeMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertTimeMinInclusiveExample BEFORE INSERT ON TimeMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_ TIMEZONE_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too early'), END; CREATE OR REPLACE TRIGGER UpdateTimeMinInclusiveExample BEFORE UPDATE ON TimeMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_ TIMEZONE_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.80 shows how this can be enforced with a trigger. Note that Oracle limits the values of he check clause to constant values and does not allow calling of PL/SQL conversion functions.
CREATE TABLE TimeMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertTimeMinExclusiveExample BEFORE INSERT ON TimeMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMEZONE_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too early'), END; CREATE OR REPLACE TRIGGER UpdateTimeMinExclusiveExample BEFORE UPDATE ON TimeMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMEZONE_TZ('1 12:45:30Z+05:00', 'J HH24:MI:SS"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Time too early'), END; |
The gMonth datatype represents a Gregorian calendar month that recurs every year. Without the optional time zone, gMonth is essentially an integer datatype with a minInclusive of 0 and a maxInclusive of 11. Refer to Sections 22.4.14 and 22.4.15 for details implementing gMonth when no time-zone offsets are required.
If time-zone offsets are required, the representation becomes much more complex. Oracle9i now has a TIMESTAMP column datatype that represents both date and time with a time-zone offset. The database designer should confer with the XML schema designer to determine if time-zone values will exist in XML instances and, if so, whether the values need to be preserved or the gMonth values can be reduced to their canonical form.
Warning
Mixing gMonth values that have time zones with those that do not in the same database table column is problematic. From an XML schema perspective, the values are only partially ordered since the time zone is not specified in some cases. However, when using a database TIMESTAMP WITH TIME ZONE column datatype, Oracle9i will default the time zone to the time zone of the database server that results in a complete ordering. Separating gMonth values that have time zones and those that do not into two database columns is advisable.
Listing 22.81 implements a gMonth table creation.
CREATE TABLE GMonthExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a gMonth datatype, Oracle needs to convert the datatype from its XML Schema format to an internal Oracle representation. In addition, because Oracle needs a date for the insert, it is appropriate to use a date that indicates that no date was specified. Oracle has a minimum date of January 1, 4712 B.C. It is represented by the number 1 and is translated into January 1, 4712 B.C. by the ‘J’ dateTime format element. A hard-coded date is especially useful when min/max inclusion/exclusion facets have been specified. Listing 22.82 is a SQL statement that inserts a gMonth into the GMonthExample table (?gMonthValue is a gMonth bind variable without the time zone and ?timezoneValue is the time-zone bind variable). FROM_TZ converts a time zone without a time-zone indicator to one with a time-zone indicator.
INSERT INTO gMonthExample (value) SELECT FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), ?gMonthValue - 1) AS TIMESTAMP), ?timezoneValue) FROM dual; |
Listing 22.83 is a SQL statement that retrieves all of the values from the GMonthExample table.
SELECT TO_CHAR(value, 'MM"Z"TZH:TZM') FROM GMonthExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.84 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthMaxInclusive BEFORE INSERT ON GMonthMaxInclusiveExample FOR EACH ROW WHEN (new.value > FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonth too late'), END; CREATE OR REPLACE TRIGGER UpdateGMonthMaxInclusive BEFORE UPDATE ON GMonthMaxInclusiveExample FOR EACH ROW WHEN (new.value > FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, ''GMonth too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.85 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthMaxExclusive BEFORE INSERT ON GMonthMaxExclusiveExample FOR EACH ROW WHEN (new.value >= FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonth too late'), END; CREATE OR REPLACE TRIGGER UpdateGMonthMaxExclusive BEFORE UPDATE ON GMonthMaxExclusiveExample FOR EACH ROW WHEN (new.value >= FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonth too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.86 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthMinInclusive BEFORE INSERT ON GMonthMinInclusiveExample FOR EACH ROW WHEN (new.value < FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonth too early'), END; CREATE OR REPLACE TRIGGER UpdateGMonthMinInclusive BEFORE UPDATE ON GMonthMinInclusiveExample FOR EACH ROW WHEN (new.value < FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonth too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.87 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthMinExclusive BEFORE INSERT ON GMonthMinExclusiveExample FOR EACH ROW WHEN (new.value <= FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonth too early'), END; CREATE OR REPLACE TRIGGER UpdateGMonthMinExclusive BEFORE UPDATE ON GMonthMinExclusiveExample FOR EACH ROW WHEN (new.value <= FROM_TZ(CAST(ADD_MONTHS(TO_DATE('1', 'J'), 10) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonth too early'), END; |
The gDay datatype represents a Gregorian calendar day of the month. Without the optional time zone, gDay is essentially an integer datatype that has a minInclusive of 1 and a maxExclusive of 31. Refer to Sections 22.4.14 and 22.4.15 for details implementing gDay when no time-zone offsets are required.
If time-zone offsets are required, the representation becomes much more complex. Oracle9i now has a TIMESTAMP column datatype that represents both date and time with a time zone. The database designer should confer with the XML schema designer to determine if time-zone values will exist in XML instances and, if so, whether the values need to be preserved or the gDay values can be reduced to their canonical form. Listing 22.88 implements a gDay table creation.
CREATE TABLE GDayExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a gDay datatype, Oracle needs to convert the datatype from its XML Schema format to an internal Oracle representation. Listing 22.89 is a SQL statement that inserts a gDay into the GDayExample table (?gDayValue is a gDay bind variable).
INSERT INTO gDayExample (value) BEGIN SELECT TO_TIMESTAMP_TZ(?gDayValue, 'J"Z"TZH:TZM') FROM dual; |
Listing 22.90 is a SQL statement that retrieves all of the values from the GDayExample table.
SELECT TO_CHAR(value, 'DD"Z"TZH:TZM') FROM GDayExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.91 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GDayMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGDayMaxInclusive BEFORE INSERT ON GDayMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GDay too late'), END; CREATE OR REPLACE TRIGGER UpdateGDayMaxInclusive BEFORE UPDATE ON GDayMaxInclusiveExample FOR EACH ROW WHEN (new.value > TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.92 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GDayMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGDayMaxExclusive BEFORE INSERT ON GDayMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GDay too late'), END; CREATE OR REPLACE TRIGGER UpdateGDayMaxExclusive BEFORE UPDATE ON GDayMaxExclusiveExample FOR EACH ROW WHEN (new.value >= TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GDay too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.93 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GDayMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGDayMinInclusive BEFORE INSERT ON GDayMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GDay too early'), END; CREATE OR REPLACE TRIGGER UpdateGDayMinInclusive BEFORE UPDATE ON GDayMinInclusiveExample FOR EACH ROW WHEN (new.value < TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GDay too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.94 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GDayMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGDayMinExclusive BEFORE INSERT ON GDayMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GDay too early'), END; CREATE OR REPLACE TRIGGER UpdateGDayMinExclusive BEFORE UPDATE ON GDayMinExclusiveExample FOR EACH ROW WHEN (new.value <= TO_TIMESTAMP_TZ('20Z05:00', 'J"Z"TZH:TZM')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GDay too early'), END; |
The gMonthDay datatype represents a Gregorian calendar day in a year. Oracle9i now has a TIMESTAMP column datatype that represents both date and time with a time-zone offset. The database designer should confer with the XML schema designer to determine if time-zone values will exist in XML instances and, if so, whether the values need to be preserved or the gMonthDay values can be reduced to their canonical form. Listing 22.95 implements a gMonthDay table creation.
CREATE TABLE GMonthDayExample ( value TIMESTAMP WITH TIME ZONE ); |
To insert a row into the table with a gMonthDay datatype, Oracle needs to convert the datatype from its XML Schema format to an internal Oracle representation.
Listing 22.96 is a SQL statement that inserts a gMonthDay into the GMonthDayExample table, including offsetting for the time-zone indicator. In addition, because Oracle automatically uses today’s year for the insert by default, it is appropriate to use a date that indicates that no date was specified. Oracle has a minimum date of January 1, 4712 B.C. It is represented by the number 1 and is translated into January 1, 4712 B.C. by the ‘J’ dateTime format element.
INSERT INTO GMonthDayExample (value) SELECT FROM_TZ(CAST(ADD_MONTHS(TO_DATE(?dayValue, 'J'), ?monthValue - 1) AS TIMESTAMP), ?timezoneValue) FROM dual; |
The ?dayValue bind variable is the day portion of a gMonthDay, the ?monthValue bind variable is the month portion of a gMonthDay, and the ?timeZone bind variable is the time-zone portion in a gMonthDay.
Listing 22.97 is a SQL statement that retrieves all of the values from the GMonthDayExample table.
SELECT TO_CHAR(value, 'MM-DD"Z"TZH:TZM') FROM GMonthDayExample; |
Refer to Section 22.2.1 for design and implementation issues.
Refer to Section 22.2.2 for design and implementation issues.
Refer to Section 22.2.3 for design and implementation issues.
There is no built-in database support for the maxInclusive constraining facet, but Listing 22.98 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthDayMaxInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthDayMaxInclusive BEFORE INSERT ON GMonthDayMaxInclusiveExample FOR EACH ROW WHEN (new.value > FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonthDay too late'), END; CREATE OR REPLACE TRIGGER UpdateGMonthDayMaxInclusive BEFORE UPDATE ON GMonthDayMaxInclusiveExample FOR EACH ROW WHEN (new.value > FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'Date too late'), END; |
There is no built-in database support for the maxExclusive constraining facet, but Listing 22.99 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthDayMaxExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthDayMaxExclusive BEFORE INSERT ON GMonthDayMaxExclusiveExample FOR EACH ROW WHEN (new.value >= FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonthDay too late'), END; CREATE OR REPLACE TRIGGER UpdateGMonthDayMaxExclusive BEFORE UPDATE ON GMonthDayMaxExclusiveExample FOR EACH ROW WHEN (new.value >= FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonthDay too late'), END; |
There is no built-in database support for the minInclusive constraining facet, but Listing 22.100 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthDayMinInclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthDayMinInclusive BEFORE INSERT ON GMonthDayMinInclusiveExample FOR EACH ROW WHEN (new.value < FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonthDay too early'), END; CREATE OR REPLACE TRIGGER UpdateGMonthDayMinInclusive BEFORE UPDATE ON GMonthDayMinInclusiveExample FOR EACH ROW WHEN (new.value < FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonthDay too early'), END; |
There is no built-in database support for the minExclusive constraining facet, but Listing 22.101 shows how this can be enforced as a trigger. Note that Oracle limits the values of the check clause to constant values and does not allow calling PL/SQL conversion functions.
CREATE TABLE GMonthDayMinExclusiveExample ( value TIMESTAMP WITH TIME ZONE ); CREATE OR REPLACE TRIGGER InsertGMonthDayMinExclusive BEFORE INSERT ON GMonthDayMinExclusiveExample FOR EACH ROW WHEN (new.value <= FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonthDay too early'), END; CREATE OR REPLACE TRIGGER UpdateGMonthDayMinExclusive BEFORE UPDATE ON GMonthDayMinExclusiveExample FOR EACH ROW WHEN (new.value <= FROM_TZ(CAST(ADD_MONTHS(TO_DATE(5, 'J'), 8) AS TIMESTAMP), '+05:00')) BEGIN RAISE_APPLICATION_ERROR(-20000, 'GMonthDay too early'), END; |
18.119.157.247