Chapter 05 - Creating Database Tables and Using Datatypes


  • Create database tables - You'll learn how to create tables using a Transact-SQL statment or a Windows application.

  • Define table columns by using different datatypes appropriate for the data to be stored in the columns - Microsoft SQL Server has many datatypes for you to choose from for the storage of your data.

  • Define a table column to permit or disallow null entries - You can choose to leave the value of a table column undefined rather than be store a value.

  • Define and use user-defined datatypes - SQL Server permits you to define your own datatypes facilitating the subsequent definition of table columns and other database structures.

  • Add rows to a table using an insert statement - The insert statement provides a simple way to store rows in a database table.

  • Define and use table and column constraints - Version 6 of SQL Server added the ability to define several characteristics of columns when the table is created rather than later using separate statements.

Data-processing systems involve the storage, processing, and retrieval of information. You must define where data will be stored before it can be processed and retrieved. All units of information, from characters to the logical definition of the entire database, can be defined through SQL Server components.

Creating Tables

Data in a relational database such as Microsoft SQL Server is stored in tables that are two-dimensional arrays. You'll recall from Chapter 1 that you've had experience working with tables from everyday life (such as train or bus schedules). The columns and rows of a table are already familiar to database users. Tables were chosen as the logical structure for storing data because of their familiarity to users and ease of use for retrieving, displaying, and manipulating data.

You can create Microsoft SQL Server database tables with the CREATE TABLE Transact-SQL statement. You can create up to 2 billion tables in each database. The major part of the creation of a table is the definition of the datatypes for columns. The syntax for CREATE TABLE is as follows:

CREATE TABLE [[database.]owner.]table_name
(column_name datatype [not null | null] IDENTITY[(seed, increment)][constraint]
[, column_name datatype [not null | null IDENTITY[(seed, increment)]]].
[constraint]...)
 [ON segment name]

For more information on identity columns, see the identity Property section later in this chapter.

Enter the name of the table (table_name) following the keywords CREATE TABLE. You can use up to 30 characters to name a database object, such as a table. The column names are entered within parentheses. You define the name and type of the column by entering a column name up to 30 characters long, followed by a Transact-SQL datatype.


Everything stored in a database is stored as an object. A database object, such as a table, has information kept about it in system tables. For example, a table created by you has the name of the table, the type of data that is stored in its columns, and other characteristics stored in the system table sysobjects.

Optionally, you can enter the database in which the table is created, as well as an owner of the table. You'll find it more convenient to define the current database in which you're working first with a USE database-name command. After you define your current database with the USE command, all subsequent commands are performed within the database specified with the USE command.


Your SQL Server account is defined with a current database. The default database that you're directed to should be the one in which you work exclusively or more often.

If you don't enter the name of the owner of an object (such as a table) when you create a table, you'll be its owner. Often, tables are created from the system administrator's (sa) account, to restrict subsequent access to the tables.

The owner of the database in which the table is defined is automatically granted the CREATE TABLE permission, which allows the creation of tables. The database owner or the sa can grant CREATE TABLE permission to other users so that they can create tables in the database. You don't have to grant permission to create temporary tables in a database.

You can use ON segment_name to specify the segment on which the table is created. The segment must have already been created and assigned to a logical device. If you later create a non-clustered index for your table and don't specify a segment, the table and the non-clustered segment are placed in the default segment.

One use you might find for defining specific segments for a table is the separation of the table information and the clustered indexes that support it. You can optimize the system by placing the table on a specific segment that's different from its non-clustered indexes. If you specify the creation of a table and its non-clustered index on different segments, SQL Server can perform queries that use the index faster.

Queries that use a table's non-clustered index can be performed faster if both are located on different segments, and if the segments are on different logical devices located on different physical disks. Sets of information (such as an index and its associated table) that are stored on different physical disks can be accessed faster than a set stored on a single disk.

Data can be referenced faster because the underlying operating system and disk subsystems can perform a large part of the data transfer from two physical disks simultaneously. Sets of data located on a single physical disk must be accessed separately just due to physical layout of the disk drive.

See the section entitled NONCLUSTERED in Chapter 10.


You can obtain good performance with SQL Server databases without specifying multiple storage segments by using disk arrays that automatically spread data across multiple disks. For more information on this, investigate setting up striped disk sets at the operating system level. This capability is transparent to, and independent from, SQL Server.

Understanding Datatypes

The major part of defining a table is specifying the datatypes for the columns of the tables. Transact-SQL allows you to define several datatypes, including those for the storage of characters, numbers, and bit patterns. You can also define your own datatypes for use in stored procedures, tables, and other work that you'll be doing with the database tables.

You must define at least one column for a table, and you can define up to 250 columns. You're also limited to a maximum row length of 1,962 bytes.


You can use image or text datatypes to get around the 1,962-byte limit for rows. Columns that are defined by using the image and text datatypes are stored outside the table and can store more than 2 billion bytes of data.

You should be careful to follow the rules for relational database design whenever feasible, however, to ensure the optimum response time and use of your SQL Server engine. For more information on database design, refer to Chapter 2, "Understanding Data Modeling and Database Design"

Tables are created by using a unit of measure called an extent. When you create a new table, the allocation of space for the table is initially set at one extent, which is eight pages, each of which is 2K in size. When the table fills the space in the already allocated extents, additional extents are automatically implemented up to the space allocated to the overall database size.


Use the system procedure sp_spaceused to obtain a report on the space allocated to a table or the graphical display in SQL Enterprise Manager. See Chapter 4, "Creating Devices, Databases, and Transaction Logs" for infomation on displaying allocated space.

Creating Temporary Tables

Creating a temporary table is a useful technique that you should take advantage of. You can create two types of temporary tables in SQL Server: local and global. A local temporary table is created if the first character of the table name is a pound sign (#). A local temporary table can be accessed only by the session in which it was created. A local temporary table is automatically dropped when the session in which it was created ends. You can't use more than 20 characters, including the pound sign, to name a local temporary table.

You create a temporary table that can be accessed by any session (each session can be created by a different user on a different client system) by defining a table with two pound signs (##) as the first two characters of the table name. A temporary table that's accessible from multiple sessions is called a global temporary table and is automatically dropped when the last session that's using it ends.


Constraints can be defined for temporary tables but foreign key constraints are not enforced.

You can use temporary tables to store sets of data that need to be operated on before permanently storing. For example, you can combine the results of the data from multiple data sets into a temporary table, and then access the combined data in the temporary table throughout your session. Data that has already been combined in a temporary table can be accessed faster than data that must be dynamically accessed from multiple tables. A temporary table that combines the results of two tables is faster to access because SQL Server doesn't need to reference the database tables to retrieve the information.

See the section entitled Performing Relational Joins in Chapter 7


Sessions are established differently depending on how you'll be accessing the server. A session is associated with a live connection to the database. For example, if you query a table and use a Dynaset type of dataset—that is, one that's active and updatable—the session will remain active. If you then connect to the database for another dataset inquiry, a separate and distinct session will be opened. Information in the temporary tables from the first session won't be available to the second session.

You might also find it convenient to use a temporary table to make a set of data available to a stored procedure that's invoked from another procedure. You'll find it easier to make data available to another procedure within a temporary table rather than pass data as a set of parameters.

See Chapter 13, "Managing Stored Procedures and Using Flow-Control Statements."

Selecting Datatypes

The datatype is the first characteristic you define for the column of a table. The datatype of a column controls the type of information that can be stored within the column. Define the datatype by following the column name with a keyword that may also require some parameters. After you define the datatype of a table column, it's stored as a permanent characteristic and can't be changed.

You can also use datatypes to define other data-storage structures, such as parameters and local variables. Parameters and local variables are storage structures defined in RAM rather than on disk. You're limited to a subset of the datatypes for the definitions of parameters and variables.

See the Chapter 13 sections entitled Using Parameters with Procedures, and Defining and Using Variables

The next sections review each of the different system-defined datatypes that you can use in the definition of your SQL Server tables and stored procedures.

Numeric integer Datatypes

Numeric integers are the first of several datatypes that you can use to define storage objects. Integer datatypes allow you to store whole numbers. You can directly perform arithmetic operations on integers without using functions. Numbers stored in integer datatypes always occupy the same amount of storage space, regardless of the number of digits within the allowable ranges for each the integer datatype.


The name of a datatype, such as integer, is case-insensitive.

int or integer

int (or integer) is the first of three integer datatypes. You can store negative and positive whole numbers within the range of –(2**31) to 2**31—approximately 4.3 billion numbers. The range is –2,147,483,648 to 2,147,483,647. Each value that's stored in an int datatype is stored in 4 bytes, using 31 bits for the size or magnitude and 1 bit for the sign.


A set of two asterisks are used to denote exponentiation. The range of numbers for numeric digits are frequently referenced using a base number raised to a power because it allows the range to be specified precisely and compactly.

smallint

smallint is the second integer datatype. You can store whole numbers within the range –32768 to +32767. Each value that's stored in a smallint datatype occupies 2 bytes and is stored as 15 bits for the magnitude and 1 bit for the sign.

tinyint

You can store only whole positive numbers in a storage structure defined as tinyint within the range 0 to 255. Each value stored as a tinyint occupies one 1 byte.

The following example shows the creation of a table with three columns. The columns are defined as the int, smallint, and tinyint datatypes. A single row is inserted into the number_example table with values within the acceptable range for storage of each datatype. select is subsequently used to retrieve the row.


Database languages and programming languages have keywords. Keywords are the words that force an action to occur in an environment. To avoid confusion and error avoid using keywords when naming tables, columns, and so on.

create table number_example
(int1 int,int2 smallint,int3 tinyint)
insert into number_example
values (400000000,32767,255)
(1 row(s) affected)

select * from number_example
int1        int2   int3
----------- ------ ----
400000000   32767  255
(1 row(s) affected)

Enforcing Value Ranges

Microsoft SQL Server automatically enforces the insertion of values within the range of each datatype. In the following two examples, values are inserted into columns that are defined as smallint and tinyint, although the values are outside the range of acceptable values.

The column values are specified in the values clause of the insert statement in the same order in which the columns were defined in the table. SQL Server returns an error message that describes the reason for the failed row insertion—the attempted insertion of a value is outside the allowable range for the datatype.

insert into number_example
values (1,32768,1)
Msg 220, Level 16, State 1
 Arithmetic overflow error for type smallint, value =  32768.
insert into number_example
values (1,1,256)
Msg 220, Level 16, State 2
 Arithmetic overflow error for type tinyint, value =  256.

Use a tinyint or smallint to store integer values in one-quarter or one-half the storage space used for storing integer values in an int datatype. These are especially useful for use as flags, status indicators, and so forth.

Numeric floating-point Datatypes

Floating-point datatypes are the second group of several numeric datatypes you can use to define storage structures such as table columns. Unlike the integer datatypes, floating-point datatypes can store decimal numbers.

Unfortunately, the floating-point datatypes are subject to the rounding error. The storage of a value in a numeric datatype that's subject to the rounding error is accurate only to the number of digits of precision that's specified. For example, if the number of digits of precision is 15, a number that's larger than 15 digits can be stored, but the digits beyond 15 may inaccurately represent the initial number inserted into the storage. Also, the number may inaccurately return results of computations that involve floating-point datatypes. The rounding error affects a number's least-significant digits—the ones at the far right. You can accurately store numbers within the number of digits of precision available in floating-point datatype.


Microsoft calls datatypes such as the floating-point datatypes approximate numeric datatypes because values stored in them can be represented only within the limitations of the storage mechanism. You should avoid performing comparisons (such as in a WHERE clause) of data that's stored in approximate datatypes because a loaded value that's larger than the number of digits of precision is altered by the rounding effect during storage.

The real Datatype

The first of the floating-point datatypes is real, which is stored in 4 bytes. You can store positive or negative decimal numbers in the REAL datatype, with up to seven digits of precision. You can store numbers in a column defined as real within the range of 3.4E–38 to 3.4E+38.

The range of values and representation is actually platform-dependent. Remember that Microsoft SQL Server evolved from the original Sybase SQL Server implementation. The REAL datatype stored on each of the several computer systems that a Sybase version was written for varied in the range of allowable characters and the actual representation of characters. For example, the range of decimal numbers stored by OpenVMS on Digital's VAX computers is 0.29E to 38 to 1.7E+38.

The underlying operating system that Microsoft SQL Server runs on is supported on Intel, MIPS, PowerPC, and Alpha AXP systems. You should consider the previously stated value—the range of 3.4E–38 to 3.4E+38—as approximate and check the range of allowable numbers for the floating-point datatype that's stored in 4 bytes on your Windows NT system.

You should also realize that data stored in floating-point datatypes, which is moved between different NT platforms with different processor architectures, may require conversion to compensate for different representations and range of values.

float[(n)] Datatypes

The second of the floating-point datatypes is float, which is stored in 8 bytes if a value for n is omitted. You can store positive or negative decimal numbers in the float datatype with as many as 15 digits of precision. You can store numbers in a column defined as float within the range of 1.7E–308 to 1.7E+308.

If you specify a value for n within the range of 1 to 7, you're actually defining a real datatype. If you specify a value within the range of 8 to 15, the datatype has the identical characteristics as if n were omitted.

In the following example, a table is created with two columns defined as real and float. A single row is added with identical numbers that are subsequently added to each column of the table. The retrieval of the row from the table shows that the number stored in the real column was stored accurately to only 7 digits, the maximum number of digits of precision for a real datatype. The same 11-digit number was stored correctly in the column defined with the datatype float because float allows up to 15 digits to be stored accurately.

create table precision_example
(num1 real,num2 float)
insert into precision_example
values (4000000.1234,4000000.1234)
select * from precision_example
num1                 num2
-------------------- --------------------
4000000.0            4000000.1234
(1 row(s) affected)

decimal[(p[, s])] and numeric[(p[, s])] Datatypes

You can use either the name decimal or numeric to select a datatype that, unlike float or real, allows the exact storage of decimal numbers. The scale and digits of precision is specified in the arguments p and s. You can store values within the range 10**38–1 through –10**38 using 2 to 17 bytes for storage.

Use p to define the number of digits that can be stored to the left and right of the decimal point. Use s to define the number of digits to the right of the decimal point that must be equal to or less than the value of p. If you omit a value for p, it defaults to 18; the default of s is 0. Table 5.1 shows the number of bytes that are allocated for the specified precision (value of p).

Table 5.1—Number of Bytes Allocated for Decimal/Numeric Datatypes
Bytes AllocatedPrecision
21–2
33–4
45–7
58–9
610–12
713–14
815–16
917–19
1020–21
1122–24
1225–26
1327–28
1429–31
1532–33
1634–36
1737–38

The following example shows the storage and subsequent retrieval of a single row stored with the columns of a table defined as numeric/decimal datatypes. This example shows the default precision and scale and an explicit precision and scale being displayed.

create table definition_example
(num1 decimal,num2 numeric(7,6))
insert into definition_example
values (123456789123456789,1.123456)
select * from definition_example
num1                   num2
---------------------- -----------
123456789123456789     1.123456
(1 row(s) affected)

The maximum precision permitted in the numeric/decimal datatypes is 28 unless you start SQL Server from the command line and change the precision. Use the command sqlservr with the option /p, which has the following syntax:

sqlservr [/dmaster_device_path][/pprecision_level]
For example, the following command starts SQL Server with a maximum precision of 38:

sqlservr /dg:\sql60\data\master.dat /p38.
If no value is specified after the precision qualifier /p, the precision for the numeric/decimal datatype is set to the maximum of 38.

Character Datatypes

You'll frequently use character datatypes to define table columns or other storage structures. Character datatypes allow the storage of a wider variety of symbols than numeric datatypes. Character datatypes enable you to store letters, numeric symbols, and special characters such as ? and >. You enter character data in either single or double quotation marks ([pr] or [dp]) when loading it into a storage area such as the column of a table.

char Datatype

char is the first type of character datatypes. When you store data in a char datatype, each symbol or character stored uses 1 byte. The number in parentheses specifies the size of storage for all sets of characters. For example, if you define a table column as the datatype char(15), each value of the column is 15 bytes in size and can store 15 characters. If you enter less than 15 characters, SQL Server adds blanks after the last specified character.

You can define a char(n) datatype to contain up to a maximum of 255 characters. Remember, the column value always contains the specified number of characters. SQL Server automatically adds spaces to the end of a char value to fill the defined length of space.


If a column is defined char and allowed to be null it will be treated as a varchar column.

Although the default installation of Windows NT uses the ASCII code to store character datatypes, you can install Windows NT so that UNICODE is used instead of ASCII. UNICODE stores character symbols in 16 bits (2 bytes) rather than ASCII's 1 byte. If Windows NT is defined to use UNICODE instead of ASCII, you must confirm the size of each character for character datatypes in applications such as Microsoft SQL Server.

varchar Datatype

You can use the varchar datatype to store a variable-length string of up to 255 characters. Unlike the char datatype, the storage space used varies according to the number of characters stored in each column value of rows of the table.

For example, if you define the table column as varchar(15), a maximum of 15 characters can be stored in the corresponding column of each table row. However, spaces aren't added to the end of the column value until the size of each column is 15 bytes. You can use a varchar to save space if the values stored in a column are variable in size. You can also specify a varchar datatype using the keyword char varying.

Using Character Datatypes

In the following examples, a table is created with two columns defined as char and varchar datatypes. The inserted row stores only two characters in each column of the row. The first column is padded with three spaces so that it occupies 5 bytes of storage. The second column of the row isn't padded and occupies only 2 bytes of storage to store the two characters. The retrieval of the row in the example displays each column value identically, masking the underlying storage difference.

create table string_example
(char1 char(5),char2 varchar(5))
insert into string_example
values ('AB','CD')
select * from string_example
char1 char2
----- -----
AB    CD
(1 row(s) affected)
In the following example, a row is inserted into the table that contains column values that are longer by one character than the maximum length of the datatypes of the table columns. The select statement in the example shows that the column values of the inserted row were truncated, or cut off, and contain only the first five characters of the column values. You don't receive a message that the truncation occurs when a row is inserted.

insert into string_example
values ('abcdef','abcdef')
select * from string_example
char1 char2
----- -----
AB    CD
abcde abcde
(2 row(s) affected)

Use the text datatype, which will allow the storage of more than 4 billion characters to store sets of characters that are longer than 255 characters.


Every time that I enter the name of a long department into the department column of a table, the department name is cut off at the end. Why?

When a table column is defined using the char or varchar datatype, the maximum length is specified for all values that are later inserted into the column. SQL Server will automatically truncate (cut off) all characters that are longer than the maximum length that was defined. SQL Server doesn't notify you that's the truncation is being performed.

I've created several reports that I produce with code in my application. On some columns the values line up fine, but on others, they aren't lined up. What's causing the problem?

When you use char datatypes, the fields are padded with extra spaces to fill the entire defined space for the column. These columns should appear fine on your report, depending on how you've read the values from SQL Server. If the extra spaces in the field are the problem, either use a trim statement in your query or store the data as a varchar.

datetime and smalldatetime Datatypes

The datetime and smalldatetime datatypes store a combination of the date and time. You'll find it more convenient to store dates and times in one of the date and time datatypes rather than a datatype such a char or varchar. If you store data in one of these datatypes, you can easily display them because SQL Server automatically formats them in a familiar form. You can also use specialized date and time functions to manipulate values stored in this manner.

If you store date and time in char or varchar (or if you store time in numeric datatypes), date and time values aren't automatically formatted in conventional ways when they're displayed.

datetime Datatype

datetime is the first type of date and time datatypes that you can use to define storage structures such as table columns. In the datetime datatype, you can store dates and times from 1/1/1753 AD to 12/31/9999 AD.

The total storage of a datetime datatype value is 8 bytes. SQL Server uses the first 4 bytes to store the number of days after or before the base date of January 1, 1900. Values that are stored as negative numbers represent dates before the base date; positive numbers represent dates since the base date. Time is stored in the second 4 bytes as the number of milliseconds after midnight.


datetime values are stored to an accuracy of 1/300th of a second (3.33 milliseconds) with values rounded downward. For example, values of 1, 2, and 3 milliseconds are stored as zero milliseconds; the values of 4 through 6 milliseconds are stored as 3 milliseconds.

When you retrieve values stored in datetime, the default format for display is MMM DD YYYY hh:mmAM/PM—for example, Sep 23 1949 11:14PM. You must enclose datetime values in single quotation marks when they're used in an insert or other statement. You can enter either the date or time portion first, because SQL Server can recognize each portion and store the value correctly.

You can use upper- or lowercase characters for the date and one or more spaces between the month, day, and year when you enter datetime values. If you enter time without a date, the default date is January 1, 1900. If you enter the date without the time, the default time is 12:00AM. If you omit the date and the time, the default value entered is January 1, 1900 12:00 AM.

You can enter the date in several ways. Each is recognized and stored correctly by SQL Server. You can enter the date in an alphabetic format, using either an abbreviation for the month or the full name of the month. You can use or omit a comma between the day and year.

If you omit the century part of the year, decades that are less than 50 are represented as 20 and those that are 50 or more are entered as 19. For example, if you insert the year 49, the complete year stored is 2049. If you enter the year as 94, the complete year stored is 1994. You must explicitly enter the century if you want a century different from the default. You must supply the century if the day is omitted from the date value. When you enter a date without a day, the default entry is the first day of the month.

The set option dateformat isn't used if you specify the month of the year in alphabetic format. If you installed Microsoft SQL Server with the US_English Language option, the default order for the display of datetime values is month, day, and year. You can change the default order for the display of the date portion of a datetime value using the set dateformat command.

See the section entitled Understanding Query Options in Chapter 16

You can enter dates several ways, including the following examples:

  • Sep 23 1949

  • SEP 23 1949

  • September 23 1949

  • sep 1949 23

  • 1949 sep 23

  • 1949 23 sep

  • 23 sep 1949

    The numeric format for datetime values permits the use of slashes (/), hyphens (-), and periods (.) as separators between the different time units. When you use the numeric format with a datetime value, you must specify the month, day, and year of the date portion of the value.

    In the numeric format, enter a separator between the month, day, and year entered in the order defined for dateformat. If you enter the values for a datetime datatype that's in the incorrect order, the month, day, or year will be misinterpreted and stored incorrectly. If you enter the information in the incorrect order, you may also receive an error message that tells you the date is out of range.

    The following is an example of several entries for the numeric form of the date portion of a datetime datatype value with set dateformat defined as month, day, and year and the language as US_English:

    • 6/24/71

    • 06/24/71

    • 6-24-1971

    • 6.24.1971

    • 06.24.71

    The last of the possible formats for the date portion of a datetime datatype value is unseparated four-, six-, or eight-digit values or a time value without a date value portion. The dateformat controlled through set dateformat doesn't affect datetime datatype values referenced as the unseparated digit format.

    If you enter a six- or eight-digit unseparated value, it's always interpreted in the order of year, month, and day. The month and day are always interpreted as two digits each. Four unseparated digit values are interpreted as the year, with the century and the month and day default to the first month and the first day of that month. Table 5.2 lists the possible interpretations of unseparated digit datetime datatype values:

    Table 5.2—Interpretation of Unseparated Digit Dates for datetime Datatypes
    Equivalent Representation Digitsin Alphabetic Format
    710624June 24, 1971
    19710624June 24, 1971
    1971January 1, 1971
    71Not valid
    [pr][pr]January 1, 1900 12:00AM


    I omitted the entry of a value for a table column that was defined as the datetime datatype when I added a new row. When I subsequently displayed the new row, the entry for the column defined as datetime was January 1, 1900, and 12 midnight.

    When you inserted the new row, you must have used [pr][pr] as a value for the datetime column, mistakenly thinking that it would result in a null entry. When two single quotation marks are used with no characters inserted between them as the value for either of the date and time datatypes, the entry January 1, 1900, and 12 midnight is always inserted by SQL Server.

    You must enter the time with the time units in the following order: hours, minutes, seconds, and milliseconds. You must have a colon as a separator between multiple time units to allow a set of digits to be recognized as a time rather than a date value. You can use AM or PM, specified in upper- or lowercase, to specify before or after midnight.

    You can precede milliseconds with a period or a colon, which affects the interpretation of the millisecond unit. A period followed by a single digit specifies tenths of a second; two digits are interpreted as hundredths of a second; three digits are interpreted as thousandths of a second. A colon specifies that the following digits will be interpreted as thousandths of a second. Table 5.3 shows several possible interpretations of the time portion of a datetime datatype value.

    Table 5.3—datetime Datatype Values
    TimeInterpretation
    11:2111 hours and 21 minutes after midnight
    11:21:15:87111 hours, 21 minutes, 15 seconds, and 871 thousandths of a second AM
    11:21:15.811 hours, 21 minutes, 15 seconds, and eight tenths of a second AM
    6amSix AM
    7 PMSeven PM
    05:21:15:500 AMFive hours, 21 minutes, 15 seconds, and 500 milliseconds after midnight

    smalldatetime

    smalldatetime is the second of the date and time datatypes you can use to define storage structures (such as table columns). In the smalldatetime datatype, you can store dates and times from 1/1/1900 AD to 6/6/2079 AD.

    The total storage of a smalldatetime datatype value is 4 bytes. SQL Server uses 2 bytes to store the number of days after the base date of January 1, 1900. Time is stored in the other 2 bytes as the number of minutes after midnight. The accuracy of the smalldatetime datatype is 1 minute. You can use smalldatetime to store values that are within its more-limited range and lesser precision when compared to datetime.


    Use the smalldatetime datatype instead of the datetime datatype to store values in half the storage space.

    In the following example, one column is defined by using the datetime datatype, and the second column is defined using the smalldatetime datatype. After the table is created, a minimum value is inserted into each column of a single row for the respective datatypes.

    create table date_table
    (date1 datetime,date2 smalldatetime)
    insert into date_table
    values ('Jan 1 1753','Jan 1 1900')
    select * from date_table
    date1                       date2
    --------------------------- ---------------------------
    Jan 1 1753 12:00AM          Jan 1 1900 12:00AM
    (1 row(s) affected)
    In the following example, successive insert statements insert a date that's beyond both the range of the columns defined by using the smalldatetime and the range of datetime datatypes. An error is returned as a result of both insert statements.

    insert into date_table
    values ('May 19 1994', 'Jun 7 2079')
    Msg 296, Level 16, State 3
    In the preceding example, the conversion of char to smalldatetime resulted in a smalldatetime value out of range:

    insert into date_table
    values ('Jan 1 10000','May 19 1994')
    Msg 241, Level 16, State 3
    The example shows a syntax error converting datetime from a character string.

    Specialized Datatypes

    Transact-SQL contains a set of specialized datatypes for data storage. Most of the time you'll store data in more conventional datatypes such as integer, floating-point, and character. You can store dates and times in the datetime or smalldatetime datatypes.

    Although you'll probably find that you can use the integer, floating-point, character, and date/time datatype formats for storing 90 percent of your data, in some cases you'll probably need a more custom solution.

    In these cases, you can use one or more of the specialized datatypes. For example, you may need to store only data that can be represented as true or false, yes or no. Since this is a binary condition, you may decide to create a custom datatype. As another example, you may need to store sets of data in a column that's larger than the 255-character limitation of the conventional character datatypes. Several additional datatypes are available to allow you to choose the best datatype for storing your information.

    bit

    You can use the bit datatype to store information that can be represented in only two states. A bit datatype is stored in a single bit; as a result, only two possible patterns can be stored—0 or 1. If you enter any other value than 0 or 1 in a data-storage structure such as a table column, 1 is stored. You can't define the bit datatype to allow null entries.


    While it is not explicitly stated in the SQL Server documentation, the bit datatype corresponds to the boolean datatype in other DBMSes and programming languages.

    You can also use a single byte to define up to eight different bit columns of a table by using the bit datatype. The amount of space allocated for one or more bits is a single byte, and the bit columns don't have to be contiguous. If you define nine columns of a table by using the bit datatype, 2 bytes are used for the total of nine bit datatypes.


    SQL Server stores information about columns defined using bit datatypes in the syscolumns system table by storing an offset to the bit column in the status column. You can't define an index that uses a column defined as a bit datatype.

    timestamp

    If you define a column of a table using the timestamp datatype, a counter value is automatically added to the timestamp column whenever you insert a new row or update an existing row. You can't explicitly enter a value into the column defined as a timestamp. A uniformly increasing counter value can be implicitly inserted only into a timestamp column by SQL Server.

    The counter value inserted by SQL Server into a timestamp column specifies the sequence of operations that SQL Server has performed. Values entered into a timestamp column are stored in a varbinary(8) format, not a datetime or smalldatetime format. Null values are permitted in a timestamp column by default. A timestamp value isn't a date and time, but it's always unique within the table and database. You can define only a single column of a table as a timestamp.


    Timestamps are often used to ensure that a row can be uniquely identified. If you're updating columns in a row, it's a common practice to specify the timestamp field in the where clause of your update statement. This will make sure that you update only one row of the table. You can be assured of the uniqueness of the value because the server will maintain and update it any time you insert or update a row.

    Timestamps are also used—again, as part of the where clause—to prevent two people from updating the same row. Because the timestamp is updated automatically whenever an update is made to the row, you can be sure that you're not going to overwrite someone else's information. If someone else updates a row that you're now working on, when they save their update, the row's timestamp will be updated, no longer matching your copy. When you issue the update command to save your changes, the where clause will fail because it can't find the specific row that you retrieved. Timestamps are excellent, server-maintained ways to make sure that you have a unique row identifier.

    If you define a column with the column name timestamp and don't specify a datatype, the column is defined using the timestamp datatype. You can display the current timestamp value that's applied to the next timestamp column of a row that's updated or to a new row added using the global system variable @@dbts.


    You can use a select statement to reference the global variable @@dbts by using the syntax

    select @@dbts
    For example, the execution of this statement during the preparation of this chapter returned the following current timestamp value:

    0x01000000a3d2ae08

    binary(n)

    You can use the binary datatype to store bit patterns that consist of up to 255 bytes. Use the integer specified in parentheses to define the length of all bit patterns from 1 to 255 bytes. You must specify the size of a binary column to be at least 1 byte, but you can store a bit pattern of all zeros.

    You must enter the first binary value preceded with 0x. You can enter binary data using the characters 0 through 9 and A through F. For example, enter the value A0 by preceding it with 0x, in the form 0xA0. If you enter values greater than the length that you defined, the values are truncated. Values are also padded with zeros after the least significant digit.

    Here's another example. A column defined as binary(1) can store up to the maximum value of ff. In the following example, a table is defined with two columns with the datatypes binary(1) and binary(2). Three insert statements are used to enter successive pairs of values of 0, 1, ff, and fff in both columns.

    create table binarytable
    (x binary(1),y binary(2))
    insert into binarytable
    values (0x0,0x0)
    insert into binarytable
    values (0x1,0x1)
    insert into binarytable
    values (0xff,0xff)
    insert into binarytable
    values (0xfff,0xfff)
    select * from binarytable
    ...
    x    y
    ---- ------
    0x00 0x0000
    0x01 0x0100
    0xff 0xff00
    0x0f 0x0fff
    (4 row(s) affected)

    varbinary(n)

    You can use the varbinary datatype to store bit patterns that consist of up to 255 bytes.. You use the integer specified in parentheses to define the maximum length of all bit patterns from 1 to 255 bytes. You must specify the size of a binary column to be at least one byte, but you can store a bit pattern of all zeros.

    Unlike the binary datatype, varbinary datatype storage is limited to just enough space for the length of the actual value. Like the binary datatype, you must enter the first binary value preceded with 0x. You can enter binary data using the characters 0 through 9 and A through F. If you enter values that are greater than the maximum length you defined, the values are truncated.

    In the following example, a table is defined with two columns with the varbinary(1) and varbinary(2) datatypes. Three insert statements are used to enter successive pairs of values of 0, 1, ff, and fff in both columns.

    create table varbinarytable
    (x varbinary(1),y varbinary(2))
    insert into varbinarytable
    values (0x0,0x0)
    insert into varbinarytable
    values (0x1,0x1)
    insert into varbinarytable
    values (0xff,0xff)
    insert into varbinarytable
    values (0xfff,0xfff)
    select * from varbinarytable
    ...
    x    y
    ---- ------
    0x00 0x00
    0x01 0x01
    0xff 0xff
    0x0f 0x0fff
    Unlike the values entered into a table in which the columns are defined as binary(1) and binary(2), the values are stored in only the amount of space that's required. Values are truncated if they're greater than the maximum space defined when the table is created.

    text and image Datatypes

    Use text and image datatypes to store large amounts of character or binary data. You can store more than 2 billion data bytes in either a text or image datatype. It's wasteful to preallocate space for text or image datatypes to any significant extent, so only a portion of the space is preallocated. The remaining space is dynamically allocated.


    image datatypes are sometimes used for embedded OLE objects that are part of a row.

    text

    Use a text datatype for storing large amounts of text. The characters stored in a text field are typically characters that can be output directly to a display device such as a monitor window or a printer. You can store from 1 to 2,147,483,647 bytes of data in a text datatype.


    You can store an entire resume in a single column value of a table row.

    Your data is stored in fixed-length strings of characters in an initially allocated 2K (2,048 bytes) unit. Additional 2K units are dynamically added and are linked together. The 2K data pages are logically—but not necessarily physically—contiguous. If you use an insert statement to insert data into a column defined as text, you must enclose the data within single quotation marks.


    If you define a column using the text datatype and permit nulls, using an insert statement to place a null value in the column doesn't allocate even a single 2K page, which saves space. However, any update statement will allocate at least one 2K page for the text column regardless of any value that may or may not be supplied for that column.

    image

    You can use the image datatype to store large bit patterns from 1 to 2,147,483,647 bytes in length. For example, you can store employee photos, pictures for a catalog, or drawings in a single column value of a table row. Typically, the data stored in an image column isn't directly entered with an insert statement.

    Your data is stored in fixed-length byte strings in an initially allocated 2K (2,048 bytes) units. Additional 2K units are dynamically added and are linked together like the pages for a text column. The 2K data pages are logically—but not necessarily physically—contiguous.

    Using text and image Datatypes

    Values that are stored as either text or image datatypes are displayed just as other columns are when you use a select statement. The number of bytes displayed is limited by the global value @@Textsize, which has a default value of 4K. You can specify the null characteristic for text or image columns. A null for a text or image column of a table doesn't allocate any 2K pages of storage, unless an update is performed on a row containing the null value.

    In the following example, two table columns are defined using image and text. Values are inserted into each column of a single row using an insert statement. The row is then retrieved from the table with a select statement.

    create table imagetext_table
    (image1 image,text1 text)
    insert into imagetext_table
    values ('123456789aczx+=\','12345678aczx+=')
    select * from imagetext_table
    image1                                 text1
    -------------------------------------------------------
    0x31323334353637383961637a782b3d5c     12345678aczx+=
    
     (1 row(s) affected)
    Data in a column defined as an image datatype isn't translated from its ASCII representation automatically when it's displayed with a select statement. Data stored in a column defined as the text datatype is automatically translated to ASCII characters when the data is output with a select statement. An image column isn't meant to be direct output. It can be passed on to another program, perhaps running on a client system that processes the data before it's displayed.

    Restrictions on text and image Columns

    You'll encounter several restrictions on the use of data stored in text and image datatypes. You can define only table columns using the text or image datatypes. You can't define other storage structures (such as local variables or parameters) as text or image datatypes.

    The amount of data that can be stored in a text or image table column makes each datatype unsuitable for use or manipulation in many Transact-SQL statements. This is simply because the amount of data that would have to be manipulated is too great. You can't specify a table column in an ORDER BY, GROUP BY, or compute clause that's a text or image datatype. SQL Server won't try to sort or group a table's rows using a column that can contain more than 4 billion bytes of data because too much data would have to be moved around and too large a space would have to be allocated in which to order the rows.

    You also can't use a text or image column in a union unless it's a union all. You can't use a subquery that returns data values from a text or image datatype. You also can't use a text or image column in a where or having clause, unless the comparison operator like is used. You can't specify distinct followed by a table column that's defined as a text or image datatype.

    Finally, you can't create an index or a primary or foreign key that's defined using a table column that you've defined as an image or text datatype.

    money Datatype

    The money datatype stores monetary values. Data values stored in the money datatype are stored as an integer portion and a decimal-fraction portion in two 4-byte integers. The range of values that you can store in the money datatype is from –922,337,203,685,477.5808 to 922,337,203,685,477.5807. The accuracy of a value stored in the money datatype is to the ten-thousandth of a monetary unit. Some front-end tools display values stored in the money datatype rounded to the nearest cent.

    smallmoney Datatype

    The smallmoney datatype stores a range of monetary values that's more limited than the money datatype. The values you can store in the smallmoney datatype ranges from –214,748.3648 to 214,748.3647. Data values stored in the smallmoney datatype are stored as an integer portion and a decimal-fraction portion in 4 bytes. Like values stored in a table column defined by using the money datatype, some front-end tools display values stored in the smallmoney datatype rounded to the nearest cent.


    You can store your monetary values in half the storage space if you choose the smallmoney rather than the money datatype.

    When you add values to a table column defined as money or smallmoney, you must precede the most-significant digit with a dollar sign ($) or a sign of the defined monetary unit.

    In the following example, a table is created with two columns that are defined using the money and smallmoney datatypes. In the first insert statements, values are incorrectly added because they aren't preceded with a dollar sign. A select statement shows that the values displayed are identical to those that were stored.

    create table monetary_table
    (money1 money,money2 smallmoney)
    insert into monetary_table
    values (16051.3455,16051.3455)
    select * from monetary_table
    money1                     money2
    -------------------------- --------------------------
    16,051.35                  16,051.35
    (1 row(s) affected)
    In a continuation of the same example, a three-digit monetary value is added to both table columns, followed by a value that's outside the storage bounds for the datatype on the computer architecture.

    insert into monetary_table
    values ($123,$123)
    insert into monetary_table
    values (922337203685477,214748.3647)
    Msg 168, Level 15, State 1
    The integer value 922337203685477 is out of the range of machine representation, which is 4 bytes.

    A large monetary value, which is defined as a money datatype, is added to the first column. It's incorrectly entered because it isn't preceded by a dollar sign ($). The select statements show that the number is stored incorrectly. If you enter a value into a table column that's defined as money or smallmoney, it's stored as a floating-point datatype, which makes it subject to the rounding error.

    insert into monetary_table
    values (922337203685476.,0)
    money1                     money2
    -------------------------- ---------
    16,051.35                  16,051.35
    123.00                     123.00
    922,337,203,685,475.98     0.00
    In the following example, the same large number that was previously entered without a dollar sign has been correctly entered with the dollar sign. A subsequent select statement shows that the large monetary value was correctly stored.

    insert into monetary_table
    values ($922337203685476.,0)
    select * from monetary_table
    money1                     money2
    -------------------------- ---------
    16,051.35                  16,051.35
    123.00                     123.00
    922,337,203,685,475.98     0.00
    922,337,203,685,476.00     0.00
    Added to the table are values that contain four digits to the right of the decimal place. When the values are subsequently displayed with a select statement, the values are displayed to two decimal places, the nearest cent.

    insert into monetary_table
    values ($922337203685477.5807,$214748.3647)
    select * from monetary_table
    money1                     money2
    -------------------------- ---------
    16,051.35                  16,051.35
    123.00                     123.00
    922,337,203,685,475.98     0.00
    922,337,203,685,476.00     0.00
    922,337,203,685,477.58     214,748.36

    sysname Datatype

    The sysname datatype is a user-defined datatype that's defined as varchar(30) and doesn't allow nulls. sysname is used for defining columns in system tables. You shouldn't use sysname to define the datatype of columns in your tables. You can use varchar(30), or you can define your own user-defined datatypes. See the section entitled Creating User-Defined Datatypes later in this chapter.

    Understanding null and not null

    Now that you've learned about the additional datatypes that can be defined for Transact-SQL storage structures such as columns, parameters, and local variables, you should understand a second characteristic that you can define. In addition to specifying the datatype of a table column, you can specify an additional characteristic for each datatype: null or not null.

    The null characteristic for a table column allows you to omit the entry of a column value in the column. If you define the characteristic for a column as not null, Microsoft SQL Server won't allow you to omit a value for the column when you insert a row. The null characteristic provides a type of validation.

    The default characteristic for a column is not null, which doesn't allow an undefined column value. A null that's defined for a column is stored differently than a space, a zero, or a null ASCII character (which is all zeros). The interpretation of a null entry is undefined or unavailable because no explicit or implicit value is assigned to the column when a row is inserted.

    If you reference a row that contains a null, the entry (null) is displayed in place of a column value to indicate that there's no entry in the row for that column.

    There are two ways to designate that a column or storage structure contains a null:

    • If no data is entered in the row for that column and there's no default value for the column or datatype, a null is entered automatically. You can define a default value that's inserted automatically into the table column when a column value is omitted. A default value can be added in place of a null.

    • You can enter a null explicitly by using null or null without quotation marks when a row is inserted into the table. If you enter null within quotation marks, it's stored as a literal string rather than a null.

    In the following example, a table is created that permits a null entry for numeric integer datatypes and character datatypes. A null is explicitly inserted into both columns of a single row in the table. In the following example, a select statement displays (null) for both column values of the row.

    create table nulltable
    (x int null, y char(10) null)
    insert into nulltable
    values (null,null)
    select * from nulltable
    x           y          1
    ----------- ----------
    (null)      (null)
    (1 row(s) affected)

    You can specify the keyword null in lower- or uppercase when you specify a null entry for the column of a row. The default display of a column that "contains" a null entry is "(null)".

    To continue the example, null is entered in the second column (y) because only the x column precedes the values clause. A null value is added to the second column y implicitly because no value is specified in the list port (values within parentheses separated by a comma) of the values clause.

    insert into nulltable
    (x)
    values (5)
    select * from nulltable
    x              y
    --------------------------
    5               (null)
    (2 row(s) affected)

    ANSI Support for nulls

    You can change the behavior of SQL Server to automatically permit nulls on table columns (or user-defined datatypes) if no reference to a the null characteristic is specified when a column (or user-defined datatype) is defined. You can use a set command to change the null characteristic for columns or user-defined datatypes defined during a client session. You can also change the null characteristic for an entire database using the system procedure sp_dboption.

    Use the following set command to cause nulls to be permitted automatically in table columns or user-defined datatypes:

    SET ANSI_null_DFLT_ON
    Use the following sp_dboption command to cause nulls to be permitted automatically in table columns or user-defined datatypes:

    sp_dboption database-name, 'ANSI null default', true
    ANSI nullability permits SQL Server not only to conform to a standard form of SQL but to be tailored to match the SQL dialect of other SQL used with other server databases. You can more easily use Microsoft SQL Server if you can modify the syntax and behavior of Transact-SQL to match a dialect of SQL that you've used previously. For example, if you change the default nullability of Microsoft SQL Server by defining the sp_dboption option as true, it automatically permits nulls in column definitions, like in Gupta's SQLBase database.


    If you've changed the null characteristic during a session or for a database, you can set it back to the default by using the commands

    SET ANSI_null_DFLT_OFF
    or

    sp_dboption database-name, 'ANSI null default', false

    null Manipulation

    When you compare a null value to any non-null value of a column or other data-storage structure, the result is never logically true. If you compare a null value to another null value, the result is also never a logical true. null values don't match each other because unknown or undefined values aren't assumed to be identical.

    However, rows that contain multiple null values in a column referenced in an ORDER BY, GROUP BY, or DISTINCT clause of a select statement are treated as identical values. All three clauses group together rows with identical values. ORDER BY is used to sort rows, and, in the case of nulls, all entries in the same column are sorted together. Columns containing nulls appear at the beginning of a sequence of rows that are sorted in ascending order.

    GROUP BY forms groups using identical values, and all nulls of a column are placed in a single group. The distinct keyword used in a select clause removes all duplicates from one or more column values and removes multiple null values as well. Columns that contain nulls are considered to be equal when you define an index that uses a null column.

    See the sections in Chapter 6 entitled Using an ORDER BY Clause Using a GROUP BY Clause and Using DISTINCT to Retrieve Unique Column Values

    If you perform computations with columns or other data structures that contain nulls, the computations evaluate to null. In the following example, the evaluation of the expression x=x+1 evaluates to null because the x column contains only a single row with a null defined for the x column:

    select * from nulltable
    where x=x+1
    x           y
    ----------- ----------
    (0 row(s) affected)
    The following example returns an error because a column defined as not null is compared with a null expression:

    select * from employees
    where badge=null
    Msg 221, Level 16, State 1
    A column of the datatype integer doesn't allow nulls. It may not be compared with null.

    Using ISNULL()

    ISNULL() is a system function that returns a string of characters or numbers in place of (null) when a null is encountered in a data-storage structure such as a table column. The syntax of the function is as follows:

    ISNULL(expression,value)
    The expression is usually a column name that contains a null value. The value specifies a string or number to be displayed when a null is found. In the following example, ISNULL() is used to return a number when a null is encountered in the value of a row, or return the character string 'NO ENTRY' when a null is encountered.

    select x,ISNULL(x,531),y, ISNULL(y, 'NO ENTRY')
    from nulltable
    x                       y
    ----------- ----------- ---------- ----------
    (null)      531         (null)     NO ENTRY
    (1 row(s) affected)

    You may decide that it's easier to avoid using null rather than deal with the intricacies of working with nulls. You can decide to use a specific pattern that's entered for a datatype that has the meaning of no entry or undefined, rather than null.

    identity Property

    In addition to defining the datatype of a column to allow or disallow nulls, you can define a column with the property of identity. When you define a column with the property identity, you can specify both an initial value (seed) that's automatically added in the column for the first row, and a value (increment) that's added to that last value entered for the column. When you add rows to the table, you omit entering a value for the column defined with the identity property. The value for the identity column is automatically entered by adding the increment value to the column value of the last row.

    In the following example, the second column is defined with the property identity. After two rows are added to the table, a subsequent retrieval of the table rows shows that the identity column values were generated by the identity mechanism.

    create table identity_table
    (name char(15),row_number integer identity(1,1))
    insert into identity_table
    (name)
    values ('Bob Smith')
    insert into identity_table
    (name)
    values ('Mary Jones')
    select * from identity_table
    name            row_number
    --------------- -----------
    Bob Smith       1
    Mary Jones      2
    
    (2 row(s) affected)
    You can assign the identity property only to a column that's defined with the datatypes int, smallint, tinyint, decimal(p,0), and numeric(p,0)—but not if the column permits nulls. If you omit a seed and increment value when specifying the identity property on a table column, they default to 1. Also, only a single column of a table can be defined with the property identity. The identity property doesn't guarantee that rows were be unique. You must establish a unique index on the identity column to guarantee unique table rows.


    You can use the keyword identitycol, as well as the name of the column, to reference the column of a table that's defined with the property identity.

    Creating and Using Constraints

    Constraints are defined to provide data integrity on a table and individual columns. The create table statement allows you to create primary and foreign keys, define unique column and rows, and specify check and default constraints.

    PRIMARY KEY Constraints

    You use PRIMARY KEY constraints for column integrity as well as referential integrity. The definition of a PRIMARY KEY constraint for a table has several effects. The PRIMARY KEY constraint ensures that all rows of a table are unique by ensuring that one or more columns don't permit duplicate values to be entered. A PRIMARY KEY constraint also disallows null for the column(s) that the constraint is defined on. A PRIMARY KEY constraint also creates a unique index on the column(s) defined in the constraint. A secondary effect is that the index can be used for faster retrieval of rows of the table that if no index were defined on the table.


    The definition of a PRIMARY KEY constraint on a single table doesn't by itself permit referential integrity. You must also define corresponding foreign keys in the tables whose rows will be combined with the table in which you define the PRIMARY KEY constraint.

    The syntax of the PRIMARY KEY constraint is as follows:

    CONSTRAINT constraint_name PRIMARY KEY CLUSTERED (column_name_1 column_name_n)
    In the following example, the employees table has a PRIMARY KEY constraint defined on the badge column.

    Create table employees4
    (name char(20),department varchar(20),badge integer,
    constraint badge_pays2 foreign key (badge) references employees4 (badge))

    FOREIGN KEY constraint

    A FOREIGN KEY constraint is used along with a previously defined PRIMARY KEY constraint on an associated table. A FOREIGN KEY constraint associates one or more columns of a table with an identical set of columns that have been defined as a PRIMARY KEY constraint in another table. When the column values are updated in the table in which the PRIMARY KEY constraint is defined, the columns defined in another table as a FOREIGN KEY constraint are automatically updated.

    The PRIMARY KEY and FOREIGN KEY constraints ensure that corresponding rows of associated tables continue to match so that they can be used in subsequent relational joins. The automatic updating of the corresponding columns of different tables after they're defined as PRIMARY KEY and FOREIGN KEY constraints is called declarative referential integrity, a feature added to SQL Server in version 6.0.

    The syntax of the FOREIGN KEY constraint clause is as follows:

    CONSTRAINT constraint_name FOREIGN KEY (column_name_1 column_name_n) REFERENCES 
    _table_name (column_name_1 column_name_n)
    The table named after the keyword REFERENCES is the table in which the corresponding column(s) are defined as a PRIMARY KEY constraint. In the following example, the badge column in the pays2 table is defined as a FOREIGN KEY constraint that is associated (references) with the badge column in the employees4 table.

    Create table pays
    (hours_worked integer, pay_rate integer,badge integer,
    constraint badge_pays2 foreign key (badge) references employees4 (badge))

    The corresponding columns that are defined as PRIMARY KEY and FOREIGN KEY constraints don't have to have the same names. However, it's simpler to understand the columns in different tables that are defined as associated PRIMARY KEY and FOREIGN KEY constraints if their names are identical.

    unique Constraint

    You apply the unique constraint to any table column to prevent duplicate values from being entered into the column. A restriction is that the column can't be defined as the primary key or part of the primary key of the table. The unique constraint is enforced through the automatic creation of a unique index for the table that's based on the column. In the following example, a unique constraint is applied to the badge column of the employees2 table.

    Create table employees2
    (name char(20), department varchar(20),badge integer,
    constraint badge_nodupes unique nonclustered (badge))

    check Constraint

    A check constraint limits the values that can be entered into one or more columns of a database table. You can use a check constraint, for example, to limit the range of values that can be stored in a column defined as a numeric datatypes that's smaller than the range permitted by the datatype.

    The process of associating a check with a table column is called binding. You can define and associate multiple checks with a single column. A check can be defined for a column, even though a rule is already defined on the column. In the following example, a check constraint is defined on the column department to restrict subsequent entries to valid departments.

    Create table employees5
    (name char(20), department varchar(20),badge integer
    check valid_department (department in 
    [ccc] ('Sales','Field Service','Software','Logistics')))

    check and other constraints can seem as though they duplicate the function of other mechanisms that exist in SQL Server. If you've had this perception, it's accurate. In version 6.0 of SQL Server, Microsoft changed its version of SQL, Transact-SQL, to conform to a standardized form of SQL, ANSI SQL. Although Transact-SQL already had existing ways of performing some operations, such as rules and defaults, the addition of ANSI SQL syntax to Transact-SQL added alternate ways of performing the same operation.

    You can often choose to implement a feature such as a restriction on the values that can be entered into a column in the way that you feel is the easiest to set up. For example, you can choose to restrict the values entered into the columns of a table by using a check constraint rather than by defining a rule and binding it to the column.

    However, you should investigate each alternate mechanism, because one may be more appropriate for your use. Although a check constraint is quicker and simpler to set up to restrict the column values than a rule, a rule is more flexible in one way. After you define a rule, it can be bound to a column in multiple tables. A rule may prove more useful to you if you're going to use it to restrict column values on columns that are in multiple tables.

    default Constraint

    You use a default constraint to have a value that's automatically added to a table column when no value is entered during an insert. You can define a default constraint to the most frequent value that occurs within a table column, thus relieving a user of entering the defined default constraint value when a new row is added to the table. The syntax of the default constraint clause is as follows:

    DEFAULT default_name value FOR column_name
    In the following example, a default value is specified for the department column for the employees6 table:

    Create table employees6
    (name char(20),department varchar(20),badge integer,
    DEFAULT department_default 'Sales' for department)
    You can also use a default, which you must define and then bind to a table column, to have a value automatically added to a table column. Although Microsoft recommends that you use a default constraint to add a value automatically to a table column, after a default is defined, it can be bound to columns in different tables, rather than in a single table.

    See Chapter 11, "Managing and Using Rules and Defaults"

    Microsoft recommends that you use a default constraint rather than a default when you've defining a default value for a column in a single table because a default constraint is stored with the table, rather than as a separate database object. If you drop a table, the constraint is automatically dropped. When a table isdeleted, a default is bound to a column of the table isn't deleted.


    With a default constraint, you can use a set of functions called niladic functions. A niladic function inserts a value that's generated by SQL Server.

    Niladic functions allow a system-supplied value that's information about the current user or a timestamp to be inserted when no value is specified. The ANSI niladic functions that can be used with a default constraint are current_user(), session_user(), system_user(), user(), and current_timestamp(). current_user(), session_user(), and user() return the user name stored in the database of the user issuing an insert or update. The system_user() function returns the SQL Server logon ID of the user, and the current_timestamp() function returns the date in the same form that's returned by the getdate() function.

    Creating User-Defined Datatypes

    You can define your own datatype, which can then be used as a datatype for a storage structure such as a table column. You always define a user-defined datatype as one of the existing system datatypes. A user-defined datatype allows you to define a datatype that can contain a length specification, if necessary, and a null characteristic.

    You can use a descriptive name for the user-defined datatype that describes the type of data that it contains.

    Creating User-Defined Datatypes with sp_addtype

    You can define a user-defined datatype with the system procedure sp_addtype, which uses the following syntax:

    sp_addtype user_defined_datatype_name, system_datatype, null | null
    After you define a user-defined datatype, you can use it to specify the datatype of a storage structure such as a table column. You can use the system procedure sp_help to display a user-defined datatype. You can create and then bind defaults and rules to user-defined datatypes. You bind rules and defaults to user-defined datatypes with the same procedures used for system datatypes sp_bindefault and sp_bindrule.

    An error message is generated if you specify not null for a column and don't create a default, or specify a value at insertion. You can also change the null or not null characteristic for a user-defined datatype when you define a column in a table.

    In the following example, a user-defined datatype is created by using sp_addtype. The characteristics are displayed with sp_help. A new table is created in which the column is defined using the user-defined datatype.

    sp_addtype names, 'char(15)', null
    Type added.
    sp_help names
    Type_name     Storage_type    Length Nulls Default_name    Rule_name
    ------------- --------------- ------ ----- --------------- ---------
    names          char            15     1     (null)          (null)
    create table usertype_table
    (charstring names)
    In the following example, a value is inserted into the table and subsequently retrieved. The insertion of the string resulted in a truncation of the inserted string to 15 characters. The example also displays the table in which a column is defined by using a user-defined datatype.

    insert into usertype_table
    values ('this is a string')
    select * from usertype_table
    charstring
    ---------------
    this is a strin
    (1 row(s) affected)
    sp_help usertype_table
    Name                           Owner                          Type
    ------------------------------ ------------------------------ ------------
    usertype_table                 dbo                            user table
    Data_located_on_segment        When_created
    ------------------------------ ---------------------------
    default                        May 19 1994 12:46PM
    Column_name     Type            Length Nulls Default_name    Rule_name
    --------------- --------------- ------ ----- --------------- -------------
    charstring      names           15     1     (null)          (null)
    Object does not have any indexes.
    No defined keys for this object.

    Creating User-Defined Datatypes with the Enterprise Manager

    To define a user-defined datatype by using the Enterprise Manager, follow these steps:

    1. Expand the databases by clicking on the plus (+) box next to the Databases folder. Click a database to select it. Your user-defined datatype will be created in the selected database.

    2. Choose User Defined Datatypes from the Manage menu.

    3. Enter a name in the New Name field of the Manage User-Defined Datatypes dialog box.

    4. Enter a system datatype in the Datatype field. Enter a length for the datatype in the Length field if you choose a datatype such as char or varchar. You can also decide to allow nulls, apply a previously defined rule or default value to the user-defined datatype, or specifiy an owner.

    5. Click OK to complete the creation process.

    Fig. 5.1 - You can list the user-defined datatypes from the Manage User-Defined Datatypes dialog box.

    Dropping User-Defined Datatypes

    You can use the system procedure sp_droptype to remove a user-defined datatype. The procedure uses the following syntax:

    sp_droptype typename
    You can't drop a user-defined datatype if one or more tables have a column that's defined using it. You can drop the user-defined datatype only if it isn't in use by any tables. In the following example, a user-defined datatype can't be dropped until you first drop the sole table in which a column is defined using the user-defined datatype.

    sp_droptype names
    Type is being used. You cannot drop it.
    object                  type owner             column               datatype
    ----------------------- ---- ----------------- -------------------- ---------
    usertype_table          U    dbo               charstring           names
    (1 row(s) affected)
    drop table usertype_table
    This command didn't return any data or rows:

    sp_droptype names
    Type has been dropped.
    You can also drop a user-defined datatype through the Enterprise Manager. To do so, follow these steps:

    1. Expand the databases by clicking on the plus (+) box next to the Databases folder. Click a database to select it. Your user-defined datatype will be created in the selected database.

    2. Choose User Defined Datatypes from the Manage menu.

    3. Select the user-defined datatype.

    4. Click Drop.

    Working with Datatypes

    You can't name objects with names of commands or other reserved works because datatypes are objects in the database. Datatypes are stored in the Systypes system table along with their coded values. You can use the following select statement to display the datatypes and their code value in the type column:

    select name,type
    from systypes
    order by type
    name                      type
    ------------------------- ----
    image                     34
    text                      35
    timestamp                 37
    varbinary                 37
    intn                      38
    sysname                   39
    varchar                   39
    binary                    45
    char                      47
    badge_type                47
    tinyint                   48
    bit                       50
    smallint                  52
    int                       56
    badge_type2               56
    smalldatetime             58
    real                      59
    money                     60
    datetime                  61
    float                     62
    floatn                    109
    moneyn                    110
    datetimn                  111
    smallmoney                122
    (24 row(s) affected)
    You can use sp_helpsql to display information about the characteristics of system datatypes, as shown in the following example:

    sp_helpsql 'datatype'
    helptext
    ------------------------------------------------------------------------
    Datatype
    Datatype                   Definition
    
    Binary(n)                 Fixed-length binary data. Maximum
                              length=255 bytes.
    Bit                       A column that holds either 0 or 1.
    Char(n)                   Character data. Maximum length=255
                              bytes.
    Datetime                  Dates and times with accuracy to milliseconds.
    Float                     Floating-point numbers.
    Image                     Large amounts of binary data (up to
                              2,147,483,647 characters.)
    Int                       Integers between 2,147,483,647 and
                              -2,147,483,648.
    Money                     Dollar and cent values.
    Real                      Floating point numbers with 7-digit precision.
    Smalldatetime             Dates and times with accuracy to the minute.
    Smallint                  Integers between 32,767 and -32,768.
    Smallmoney                Monetary values between 214,748.3647 and
                              -214,748.3648.
    Text                      Large amounts of character data (up to
                              2,147,483,647 characters.)
    Timestamp                 Automatically updated when you
                              insert or update a row that has a timestamp
                              column, or use BROWSE mode in a
                              DB-LIBRARY application.
    Tinyint                   Whole integers between 0 and 255.
    Varbinary(n)              Variable-length binary data. Max
                              length=255 bytes.
    Varchar(n)                Variable-length character data. Max
                              length=255 bytes.

    Creating Tables and Defining Columns Through the Enterprise Manager

    In addition to creating a table with the create table statement, you can create a table through the Enterprise Manager. To do so, follow these steps:

    See the Chapter 1 section entitled SQL Enterprise Manager

    1. Expand the databases by clicking on the plus (+) box next to the Databases folder. Click a database to select it. Your table will be created in the selected database.

    2. Choose Tables from the Manage menu.

    3. Enter a column name in the Column Name field.

    4. Use the mouse or Tab key to move to the Datatype field. Select a datatype from the list that appears.

    5. If the datatype that you've chosen requires the specification of a length, enter it into the Length field.

    6. To allow null values, leave the Nulls field selected.

    7. If you've previously defined default values, you can choose one in the Default field.

    8. Repeat steps 3 through 7 to continue specifying up to 250 columns and their characteristics. Figure 5.2 shows the Manage Tables window after information for three columns is entered.

      Fig. 5.2 - The null property is automatically enabled for each column that you enter, though you can uncheck it.

    9. Click the Save Table button on the Manage Tables toolbar to bring up the Specify Table Name dialog box.

    10. Enter a name for the new table in the Specify Table Name dialog box (see fig. 5.3).

      Fig. 5.3 - Name the table to complete the operation.

    11. Click OK. The new table appears as the selected table in the Manage Tables window.

    You also can define properties on columns such as an identity, primary key, or constraint by clicking the Advanced Features tool on the toolbar of the Manage Tables window while you're defining the table. For example, in figure 5.4, a column has been defined as an identity column in the Identity Column combo box of the Manage Tables window after the Advanced Features tool is clicked.

    Fig. 5.4 - You can specify an initial value (seed) and increment for your identity column.

    Adding Data to a Table with insert

    After you create a table, you can add data by using an insert statement. Several forms of the insert statement can be used to add new rows to a table. Each insert statement can add only a single row to a table. The complete form of the insert statement uses the following syntax:

    insert INTO table_name
    (column_name_1,...,column_name_n)
    VALUES ('string_1',...'lstring_')
    List the table columns to receive values separately, enclosed by parentheses and separated by commas, after the insert clause. Enter the values that will be added to the table columns in parentheses in the same order as the column names in the previous line. The list of column values is preceded by the VALUES keyword.

    You don't have to list the columns and their values in the same order as they were defined in the table. You must, however, enter the values in the VALUES clause in the correct order as the column names listed in the previous line; otherwise, data values may be inserted into the wrong columns.

    In the following example, the columns are listed in the order in which they were defined in the table—but they don't have to follow the same order. The values are entered in the VALUES clause in an order that corresponds to the order of the columns named in the previous line.

    insert into employees
    (name, department, badge)
    values ('Bob Smith', 'SALES', 1834)
    If you omit one or more column names, a null or default value is entered into the table row. In the following example, the name and badge columns are listed in the insert statement. The values clause omits a value for the department.

    insert into employees
    (name, badge)
    values ('Bob Mariah', 1999)
    Table 5.4 shows the resulting entry for a table if an explicit value isn't listed in the values list of an insert statement.

    Table 5.4—Effect of null and Default Values on Table Column Entries
    Column Characteristic(s)User EntryResulting Entry
    null definedNo default value definednull
    not null definedNo default value definedError, no row inserted
    null definedDefault value definedDefault value
    not null definedDefault value enteredDefault value


    Use (null) within the values list to insert a null into a column that has been defined to permit the null.

    From Here...

    In this chapter you've learned to create database tables. The process of creating a table involves the selection of the appropriate datatypes for your table columns. In addition, you've learned to add the additional characteristics that can be defined for a table column including the null characteristic and the various constraints. You learned to create, drop, and list the characteritics of a table using Transact-SQL syntax and the Enterprise Manager.

    For further discussion about topics mentioned in this chapter, see the following chapters:


    QUE Home Page

    For technical support For our books And software contact support@mcp.com

    Copyright © 1996, Que Corporation

    Table of Contents

    04 - Creating Devices, Databases, and Transaction Logs

    06 - Retrieving Data with Transact-SQL