Transact SQL

Databases Within a Server

How Databases Work

Each server contains multiple databases. Each database contains multiple tables.

A user has one logon (login id/password) for the whole server. This is called the server user name.

A user must be added to a database within the server before the user can access that database. The user's name within the database is just called a "user name".

A user has one server user name for the whole server and one database user name for each database he's been in.

Each database has one user designated as the database owner or dbo. The dbo is privileged within the database.

A server user name may not be the same as the corresponding database username. It's generally a good idea to keep them the same.

When a user logins to the database server, the user is put into his default database (different for different users).

By default an SQL query will look for a table name in the current database.

A server will often contain multiple databases. There is a "pubs" database supplied by Sybase as an example application database. It even has consistent data in it.


Transact-SQL and Sybase Databases

This section provides some sample Transact-SQL relating to multiple databases in an SQL Server. Note that "isql" (from unix) requires a "go" after each of the SQL statements.

To look at your current database:

select db_name()

Looking at the list of available databases:

sp_helpdb

Changing your default database to the pubs database:

use pubs

Doing a select against a table in the current database (assuming the current database is pubs):

select * from authors

Doing a select against a table in another database:

select name from tempdb..sysobjects select name from tempdb.dbo.sysobjects

In the above query, tempdb refers to a special database within each server. More on tempdb later. The optional "dbo" in the above query refers to the user owning the table in the tempdb database. We'll talk more about this in the security section later. For now just use the ".." notation.

The above examples use select statements, but they could just have easily used insert, update, or delete statements.

figure - pubs database block diagram


Miscellaneous Notes on Submitting SQL

From a Unix terminal login use isql to submit sql to a Sybase server. For example:

isql -Umylogin -SMYSERVER

Submitting Batches of SQL Statements

You can send more than one SQL statement at a time. For example, you can type in eight select statements and then send them all to the server at once. These eight select statements are said to be part of a batch.

In Unix, use the "go" statement in isql to send the SQL statements.

In most PC client SQL utilities, use the "send sql" button. You will need to click on the "more results" button for the results from each separate select statement.

Note that a "go" or "send" corresponds to a dbsqlexec in db-library (Open Client) C code.

Certain commands cannot be batched together (eg. use pubs).

Other Misc Sybase SQL Notes

Sybase keywords are NOT case sensitive.

Sybase object names (tables, views, etc.) ARE case sensitive.

We'll talk about security later, but for now you'll need to know that everyone has read-only access to the pubs database.

The Commands Reference Manual is the bible for Transact-SQL.


What has Sybase Done to SQL?

Structured Query Language is non-procedural. Data manipulation occurs in a single step (query).

Sybase has extended SQL with procedural control statements. The extended SQL is called Transact-SQL.

Transact-SQL includes the following procedural extensions:

Local Variables

Before we discuss the control structures like "if" and "while", we need to briefly discuss local variables.

Local variables exist within the current procedure batch. That is they exist until the next "go" (isql) or "send sql" . As we'll see later, local variables are also used by stored procedures. A local variable declaration has the following syntax:

declare {@variable_name} {datatype}

For example:

declare @myvar int declare @hisvar char(10) declare @hervar money, @itsvar float

Note that there is no end of line terminator (eg. semicolon).

Assigning Values to Local Variables

To assign values to a local variable, use the "select" statement:

select @myvar=10 select @myvar=count(*) from authors

Note, when using select statements to assign values to variables only one value must be assigned, otherwise the last value returned is used.

Using Local Variables in a Where Clause

For example:

/* select all titles which are more expensive ** than half the price of the most expensive ** title. */ declare @most_expensive_title_cost money select @most_expensive_title_cost=max(price) from titles select title from titles where price > @most_expensive_title_cost/2

Displaying Local Variables

There are two ways to display local variables which are slightly different. The first way is to use the "select" statement:

declare @myvar int select @myvar=2 select @myvar select @myvar+10

The second way to display local variables use the "print" statement. The print statement only works with character type variables:

declare @mymsg char(80) select @mymsg="This is a test" print @mymsg print "Danger Will Robinson"

With the select statement, the value of myvar is returned as part of the mainstream data. To the client it looks like the results of a select on a one row table. Also, the select statement can have expressions in it's select list.

The print statement actually sends a text message to the client error message handler (eventhough it's not an error). The client handle the output of the print statement in the same way it handles error messages from the server, usually with a pop up box.

The "print" statement is usually used for sending warning or status messages to the end user.

The select statement output must be explicitly processed by the client to be made use of. In other words, the client must be expecting the results. This is not the case with the print statement.

Note you cannot assign to and display a local variable in the same select statement.

The print statement can only take a single string parameter. In order to format a print message with parameters you must do the following:

declare @printmsg char(80) declare @myparam int select @myparam=10 select @printmsg="The value of myparam is" + convert( char(5), @myparam ) print @printmsg

Control of Flow in Transact-SQL

Conditional execution.

if {boolean_expression} {statement} else {statement}

Multiple Statements Treated as a Single Statement

begin {statement} {statement} {...} end

Conditional Looping

while {bool ean_expression} {statement}

Exit With Return Status

return {optional integer status}

IF..ELSE

if {boolen_expression} {statement} else {statement}

A boolean expression uses standard comparison operators such as:

> < => <= !=

Boolean expressions can contain local variables.

The following are examples of several types of if statements:

declare @author_cnt int select @author_cnt=count(*) from authors if @author_cnt > 20 select "There are a lot of authors in pubs db" else select "There are less than 20 authors in pubs" if @author_cnt < (select count(*) from titles) select "There are more titles than authors "

Note that in the last example any select statement can be used as long as it returns only one row of the proper type. The select statement must be enclosed in parenthesis.


IF EXISTS

A special version of the "if" statement works as follows:

if exists (select title_id from titles where price<$20.00) select "There are titles that cost less than $20"

Also:

if not exists ( select title_id from titles where price>$100000.00 ) select "All titles are affordable"

The "if exists" statement only tests to see if any rows were returned by the query. It doesn't make any difference what column is selected in the "if exists" test.


BEGIN..END

The begin..end structure permits a series of Transact-SQL statements to be treated as one from the point of view of "if" and "while" constructs.

For example, in all the previous "if" examples, only one Transact- SQL statement could be executed if the boolean condition was true. The "if" construct treats all the lines between the begin and end as if they were one statement:

if 1 = 1 begin select .... update .... print ... end /* end of the if */

Begin..end has no significance for transaction processing. It in no way affects commits or rollbacks.

WHILE..

The syntax of the "while" statement is as follows:

while {boolean expression} statement

For example:

declare @myvar int select @myvar=1 while @myvar > 0 begin { misc processing} select @myvar=count(*) from mytables where {test for unprocessed rows} end

A "break" statement in the begin..end will exit the while loop.

A "continue" statement in the begin..end will jump to the "end" and start the next iteration of the loop.

The "while" statement isn't used that much because Transact- SQL doesn't support arrays.


Looping in Transact-SQL

One common Transact-SQL coding problem involves performing processing for each row in a table. Here's an example:

declare @old_title_id varchar(6), @new_title_id varchar(6) select @old_title_id = " " /* minimum value */ while 1 = 1 /* exit from loop using "break" */ begin if not exists( select title_id from titles where title_id .gt. @old_title_id ) break /* get the next row with title_id greater than ** the last row */ select @new_title_id=min(title_id) from titles where title_id .gt. @old_title_id update title set .... where title_id = @new_title_id { do other misc processing on @new_title_id } select @old_title_id = @new_title_id end /* while loop */

System 10 will use cursors control to implement this sort of processing.


Other Transact-SQL Stuff

Data Types

The following is a table of Sybase supported datatypes:

Datatype Physical Size Legal Values Example ----------- --------------- ----------------- --------------- binary(n) n bytes Hex number 0x7f 1-255 digits 0xef0123456789 long. bit 1 bit 0 or 1 1 char(n) n 1 to 255 "This is a string" characters characters datetime 8 bytes Jan 1, 1753 00:00 "7/12/92" to "23:50 April 11,1992" Dec 31, 9999 23:59 float 8 bytes +/- 10-308 23.33E-22 or to +/- 10+307 3.14159 image actual length 2048 to graphics data of data in 2 billion bytes 2k byte blocks int 4 10 to the -231 to 231-1 1234 money 8 bytes +/-$922,337,203,685,477.5808 $50.14 smallint 2 bytes 10 to -215 to 215 -123 text actual length of 2048 to on line text data in 2k byte 2 billion retrieval blocks chars tinyint 1 byte 0 to 255 23 (positive only) varbinary(n) 1 to n bytes 1 to 255 0x1ac2 (255 bytes bytes max) varchar(n) 1 to n characters "This is a string" n=1 to 255

Sybase vs IBM Datatypes

ASCII vs EBCDIC

The Sybase server uses ASCII as does the Unix operating system. IBM's DB2 database uses EBCDIC.

As we'll see later, the Sybase Net Gateway transparently takes care of the ASCII to EBCDIC conversion for us.

ASCII and EBCDIC have different sort orders. Some applications are affected by this.

Other Datatype Issues

Pre-System 10 Sybase does not support fixed decimal numbers (System 10 has the numeric datatype). Instead, you would use the money datatype (which actually is a fixed decimal type) or the float datatype. In some cases, it may be necessary to carry the numbers as integers in Sybase (int) and manually keep track of the decimal point.

Sybase's char and varchar datatypes support a maximum string length of 255 characters. DB2's is max char string length is much longer. Sybase does support arbitrary length "text" and "image" datatypes, but there are restrictions on these (as we'll see later).


Global Variables

Sybase has special global variables which, which should really be called system variables.

These are predefined variables which hold system related information. All global variables start with a "@@"

The most interesting global (system) variables are:


raiserror

Very similar to the "print" statement with one added feature. The @@error global variable is also set by this.

The raiserror statement does not abort the current batch of command. Execution continues at the next statement after the raiserror.

Format is:

raiserror {error number} {error string} raiserror {error number} {local variable}

Error number must be greater than 20000. The @@error variable is set to {error number}

Hint, Sybase defined error codes have values less than 20000. These error codes and associated descriptions are kept in a database table called sysmessages in the master database. Here's a sample query that look for error codes:

select error, description from master..sysmessages where description like '%duplicate%' and (description like '%key%' or description like '%index%' )

Transactions

A transaction represents a set of database operations which either complete successfully or rollback so that no modifications at all are made.

Sybase transactions differ slightly from DB2 style transactions.

Sybase transactions start with an explicit "begin transaction" statement and end with the standard "commit transaction". These statements have to occur in pairs.

In DB2, there is no explicit "begin transaction". Instead, there is an implicit "begin transaction" whenever a "select", "insert", "update", or "delete" is executed. This style of transaction management is called chained transaction management.

In Sybase, if a single "select", "insert", "update", or "delete" is executed, the single statement is treated as if there is a "begin transacton" before the statement and a "commit transaction" after the statement.

The following is an example of transaction management in Sybase (you must have insert access to authors to run this):

declare @lcl_error int, @lcl_rowcount int begin transaction /* this will cause a duplicate key value error on the au_id field */ insert into authors ( au_id, au_lname, au_fname, phone, contract ) values ( "998-72-3567", "xx", "yy", "555-1212", 1 ) /* save system global variables because ** other operations will clear them */ select @lcl_error=@@error, @lcl_rowcount=@@rowcount /* if anykind of duplicate key error */ if (@lcl_error=2601 or @lcl_error=2615 or @lcl_error=2626 ) print "Please refer to help menu for choosing author id's" if (@lcl_rowcount != 0 ) begin raiserror 20233 "Overall operation aborted" rollback transaction return 233 end {other misc work} commit transaction

Transactions and Locking

Sybase locks pages (2048 bytes) not rows. This is a trade off between the speed of managing locking and level of contention that occurs at runtime.

Any writes (insert, update, delete) that occur during a transaction cause that page to be locked until the commit or rollback.

Remember that even with no "begin transaction" statement, indivdual insert, update, delete statements have implicit begin/end transaction statements around them. While these statements are running they will have pages locked.

Holdlock

By default, reads (select) are not locked. Sometimes, this is not appropriate. If you need to guarantee that a selected row doesn't change once read during a transaction, use the select with holdlock. For example:

declare @lcl_au_id char(10) begin transaction select @lcl_au_id=au_id holdlock from authors where au_id="998-72-3567" {do misc processing} commit transaction

Next chapter