Scripts and Batches
DECLARE @Ident int;
SET @TotalCost = 10
SET @TotalCost = @UnitCost * 1.1
SELECT @Test = MAX(UnitPrice) FROM Sales.SalesOrderDetail;
@@IDENTITY
Returns the last identity value inserted as a result of the last INSERT or SELECT INTO statement
@@ROWCOUNT
Returns the number of rows affected by the last statement.
A batch is a grouping of T-SQL statements into one logical unit. All of the statements within a batch are
combined into one execution plan, so all statements are parsed together and must pass a validation of
the syntax or none of the statements will execute. Note, however, that this does not prevent runtime errors
from happening. In the event of a runtime error, any statement that has been executed prior to the runtime
error will still be in effect. To summarize, if a statement fails at parse-time, then nothing runs. If a statement
fails at runtime, then all statements until the statement that generated the error have already run.
To separate a script into multiple batches, we make use of the GO statement. Because each batch is processed independently, an error in one batch does not prevent another batch from running.
Thinking that GO is a T-SQL command is a common mistake. GO is a command that is only recognized by
the editing tools (Management Studio, sqlcmd). If you use a third-party tool, then it may or may not
support the GO command, but most that claim SQL Server support will.
There are several commands that absolutely must be part of their own batch. These include:
❑ CREATE DEFAULT
❑ CREATE PROCEDURE
❑ CREATE RULE
❑ CREATE TRIGGER
❑ CREATE VIEW
USE AdventureWorks2008;
DECLARE @MyVarchar varchar(50); --This DECLARE only lasts for this batch!
SELECT @MyVarchar = ‘Honey, I’’m home...’;
PRINT ‘Done with first Batch...’;
GO
PRINT @MyVarchar; --This generates an error since @MyVarchar
--isn’t declared in this batch
PRINT ‘Done with second Batch’;
GO
PRINT ‘Done with third batch’; -- Notice that this still gets executed
-- even after the error
GO
Perhaps the most likely scenario for using batches is when precedence is required — that is, you need
one task to be completely done before the next task starts.
sqlcmd is a utility that allows you to run scripts from a command prompt in a Windows command box.
Dynamic SQL: Generating Your Code On the Fly with the EXEC Command
T-SQL offers most of the classic choices for control of flow situations, including:
❑ IF ... ELSE
❑ GOTO
❑ WHILE
❑ WAITFOR
❑ TRY/CATCH
CASE <input expression>
WHEN <when expression> THEN <result expression>
[...n]
[ELSE <result expression>]
END
WAITFOR
DELAY <’time‘> | TIME <’time‘>
BEGIN TRY
{ <sql statement(s)> }
END TRY
BEGIN CATCH
{ <sql statement(s)> }
END CATCH [ ; ]
Store Procedure
The primary benefits of sprocs include:
❑ Making processes that require procedural action callable
❑ Security
❑ Performance
Much like views, we can create a sproc that returns a recordset without having to give the user authority to the underlying table.
After the sproc has been created, it sits in wait for the first time that it is executed. At that time, the sproc
is optimized and a query plan is compiled and cached on the system. Subsequent times that we run our
sproc will, unless we specify otherwise using the WITH RECOMPILE option, generally use that cached
query plan rather than creating a new one.
EXEC spMySproc ‘1/1/2004’
WITH RECOMPILE
DROP PROC|PROCEDURE <sproc name>[;]
USE AdventureWorks2008;
GO
CREATE PROC spEmployeeByName
@LastName nvarchar(50)
AS
SELECT p.LastName, p.FirstName, e.JobTitle, e.HireDate
FROM Person.Person p
JOIN HumanResources.Employee e
ON p. BusinessEntityID = e.BusinessEntityID
WHERE p.LastName LIKE @LastName + ‘%’;
EXEC spEmployeeByName ‘Dobney’;
CREATE PROC spEmployeeByName
@LastName nvarchar(50) = NULL
AS
IF @LastName IS NOT NULL
SELECT p.LastName, p.FirstName, e.JobTitle, e.HireDate
FROM Person.Person p
JOIN HumanResources.Employee e
ON p.BusinessEntityID = e.BusinessEntityID
WHERE p.LastName LIKE @LastName + ‘%’;
ELSE
SELECT p.LastName, p.FirstName, e.JobTitle, e.HireDate
FROM Person.Person p
JOIN HumanResources.Employee e
ON p.BusinessEntityID = e.BusinessEntityID;
CREATE PROCEDURE [dbo].[uspLogError]
@ErrorLogID [int] = 0 OUTPUT -- contains the ErrorLogID of the row inserted
AS -- by uspLogError in the ErrorLog table
BEGIN
SET NOCOUNT ON;
-- Output parameter value of 0 indicates that error
-- information was not logged
SET @ErrorLogID = 0;
BEGIN TRY ……
Actually, your program will receive a return value whether you supply one or not. By default, SQL
Server automatically returns a value of zero when your procedure is complete.
Note that the return value must be an integer.
USE AdventureWorks2008;
GO
ALTER PROC spTestReturns
AS
DECLARE @MyMessage varchar(50);
DECLARE @MyOtherMessage varchar(50);
SELECT @MyMessage = ‘Hi, it’’s that line before the RETURN’;
PRINT @MyMessage
RETURN 100;
SELECT @MyOtherMessage = ‘Sorry, but we won’’t get this far’;
PRINT @MyOtherMessage;
RETURN;
DECLARE @Return int;
EXEC @Return = spTestReturns;
SELECT @Return;
@@ERROR
USE AdventureWorks2008
GO
CREATE PROC spInsertValidatedBusinessEntityContact
@BusinessEntityID int,
@PersonID int,
@ContactTypeID int
AS
BEGIN
DECLARE @Error int;
INSERT INTO Person.BusinessEntityContact
(BusinessEntityID
,PersonID
,ContactTypeID)
VALUES
(@BusinessEntityID, @PersonID, @ContactTypeID);
SET @Error = @@ERROR;
IF @Error = 0
PRINT ‘New Record Inserted’;
ELSE
BEGIN
IF @Error = 547 -- Foreign Key violation. Tell them about it.
PRINT ‘At least one provided parameter was not found. Correct and retry’;
ELSE -- something unknown
PRINT ‘Unknown error occurred. Please contact your system admin’;
END
END
Manually Raising Errors
RAISERROR (<message ID | message string | variable>, <severity>, <state>
[, <argument>
[,<...n>]] )
[WITH option[,...n]]
Extended Stored Procedures (XPs)
SQL Server does allow for the idea of externally written
code that runs as a .DLL in process with SQL Server. XPs are created using C or C++.
User-Defined Functions (UDFs)
With a sproc, you can pass parameters in and also get values in parameters passed back out. You
can return a value, but that value is really intended to indicate success or failure rather than return
data. You can also return result sets, but you can’t really use those result sets in a query without
first inserting them into some kind of table (usually a temporary table) to work with them further
Even using a table valued output parameter, you still need to make at least one additional step before
using the results in a query.
With a UDF, however, you can pass parameters in, but not out. Instead, the concept of output parameters
has been replaced with a much more robust return value. As with system functions, you can return a scalar
value — what’s particularly nice, however, is that this value is not limited to just the integer data type
as it would be for a sproc. Instead, you can return most SQL Server data types (more on this in the next
section).
As they like to say in late-night television commercials: “But wait! There’s more!” The “more” is that you
are actually not just limited to returning scalar values—you can also return tables. This is wildly powerful,
and we’ll look into this fully later in the chapter.
CREATE FUNCTION dbo.DayOnly(@Date datetime)
RETURNS varchar(12)
AS
BEGIN
RETURN CONVERT(varchar(12), @Date, 101);
END
SELECT * FROM Orders WHERE dbo.DayOnly(OrderDate) = dbo.DayOnly(GETDATE());
CREATE FUNCTION dbo.fnContactList()
RETURNS TABLE
AS
RETURN (SELECT BusinessEntityID,
LastName + ‘, ‘ + FirstName AS Name
FROM Person.Person);
GO
// parameterized view
CREATE FUNCTION dbo.fnContactSearch(@LastName nvarchar(50))
RETURNS TABLE
AS
RETURN (SELECT p.BusinessEntityID,
LastName + ‘, ‘ + FirstName AS Name,
ea.EmailAddress
FROM Person.Person as p
LEFT OUTER JOIN Person.EmailAddress ea
ON ea.BusinessEntityID = p.BusinessEntityID
WHERE LastName Like @LastName + ‘%’);
GO
Any coverage of UDFs would be incomplete without discussing determinism. If SQL Server is going to
build an index over something, it has to be able to deterministically define (define with certainty) what
the item being indexed is.
Transactions and Locks
Transactions are all about atomicity. Atomicity is the concept that something should act as a unit.
From our database standpoint, it’s about the smallest grouping of one or more statements that
should be considered to be all or nothing.
❑ BEGIN a transaction: Set the starting point.
❑ COMMIT a transaction: Make the transaction a permanent, irreversible part of the database.
❑ ROLLBACK a transaction: Say essentially that we want to forget that it ever happened.
❑ SAVE a transaction: Establish a specific marker to allow us to do only a partial rollback.
In the normal operation of your database, most activities that you perform are logged to the transaction
log, rather than written directly to the database. A checkpoint is a periodic operation that forces all dirty
pages for the database currently in use to be written to disk. Dirty pages are log or data pages that have
been modified after they were read into the cache, but the modifications have not yet been written to
disk. Without a checkpoint the log would fill up and/or use all the available disk space.
The SQL Server lock manager is that shopkeeper. When you come into the SQL Server store, the lock manager
asks what your intent is — what it is you’re going to be doing. If you say “just looking,” and no one
else already there is doing anything other than just looking, then the lock manager will let you in. If you
want to buy (update or delete) something, then the lock manager will check to see if anyone’s already there.
If so, you must wait, and everyone who comes in behind you will also wait. When you are let in to buy,
no one else will be let in until you are done.
Locks can address four major problems:
❑ Dirty reads
Dirty reads occur when a transaction reads a record that is part of another transaction that isn’t complete
yet. If the first transaction completes normally, then it’s unlikely there’s a problem. But what if the transaction
were rolled back? You would have information from a transaction that never happened from the
database’s perspective!
❑ Non-repeatable reads
A non-repeatable read is caused when you read the record twice in a transaction and a separate transaction
alters the data in the interim.
❑ Phantoms
records that appear mysteriously,
as if unaffected by an UPDATE or DELETE statement that you’ve issued. someone performed an INSERT statement at the very same time your UPDATE was running. Since it was
an entirely new row, it didn’t have a lock on it and it proceeded just fine
❑ Lost updates
Lost updates happen when one update is successfully written to the database, but is accidentally overwritten
by another transaction. Lost updates can happen when two transactions read an entire record, then one writes updated information
back to the record and the other writes updated information back to the record.
Lockable Resources
There are six lockable resources for SQL Server, and they form a hierarchy. The higher level the lock, the
less granularity it has (that is, you’re choosing a higher and higher number of objects to be locked a cascading
type of action just because the object that contains them has been locked). These include, in
ascending order of granularity:
❑ Database: The entire database is locked. This happens usually during database schema changes.
❑ Table: The entire table is locked. This includes all the data-related objects associated with that
table, including the actual data rows (every one of them) and all the keys in all the indexes associated
with the table in question.
❑ Extent: The entire extent is locked. Remember than an extent is made up of eight pages, so an
extent lock means that the lock has control of the extent, the eight data or index pages in that
extent, and all the rows of data in those eight pages.
❑ Page: All the data or index keys on that page are locked.
❑ Key: There is a lock on a particular key or series of keys in an index. Other keys in the same
index page may be unaffected.
❑ Row or Row Identifier (RID): Although the lock is technically placed on the row identifier (an
internal SQL Server construct), it essentially locks the entire row.
Lock Modes
Beyond considering just what resource level you’re locking, you also should consider what lock mode
your query is going to acquire. Just as there are a variety of resources to lock, there are also a variety of
lock modes.
Shared Locks
A shared lock is used when you need only to read the data. one thing that shared locks do is prevent users from performing dirty reads.
Exclusive Locks
Exclusive locks are not compatible with any other lock. This prevents two people from updating, deleting, or whatever at the same time.
Update Locks
Update locks are something of a hybrid between shared locks and exclusive locks. Think about it — in order to do an update, you need to validate your WHERE
clause (assuming there is one) to figure out just what rows you’re going to be updating. That means that
you only need a shared lock, until you actually go to make the physical update. At the time of the physical
update, you’ll need an exclusive lock. an update lock prevents any other update locks from being established.
deadlock: two update query finish the read and waiting to escalate to an exclusive lock. they wait for each other.
Intent Locks
Without intent locks, the higher-level objects wouldn’t even know that you had the lock at the lower
level.
Schema Locks
Bulk Update Locks
Isolation Level
The first thing to understand about the relationship between transactions and locks is that they are inextricably
linked with each other. By default, any lock that is data modification related will, once created,
be held for the duration of the transaction. If you have a long transaction, this means that your locks
may be preventing other processes from accessing the objects you have a lock on for a rather long time.
It probably goes without saying that this can be rather problematic.
However, that’s only the default. In fact, there are actually five different isolation levels that you can set:
❑ READ COMMITTED (the default)
the locks associated with the SELECT statement
are freed as soon as the SELECT statement is complete — SQL Server doesn’t wait for the end of the
transaction.
Action queries (UPDATE, DELETE, and INSERT) are a little different. If your transaction performs a query
that modifies data, then those locks will be held for the duration of the transaction (in case you need to
roll back).
By keeping this level of default with READ COMMITTED, you can be sure that you have enough data
integrity to prevent dirty reads. However, non-repeatable reads and phantoms can still occur.
❑ READ UNCOMMITTED
READ UNCOMMITTED is the most dangerous of all isolation level choices, but also has the highest performance
in terms of speed.
Setting the isolation level to READ UNCOMMITTED tells SQL Server not to set any locks and not to honor
any locks. With this isolation level, it is possible to experience any of the various concurrency issues we
discussed earlier in the chapter (most notably a dirty read).
there are actually good reasons to have this isolation level, and they almost always have to do with reporting.
By using READ UNCOMMITTED, you can often get around this problem — at least for reports where the
numbers don’t have to be exact.
You can get the same effect as READ UNCOMMITTED by adding the NOLOCK optimizer hint in your query.
❑ REPEATABLE READ
The REPEATABLE READ escalates your isolation level somewhat and provides an extra level of concurrency
protection by preventing not only dirty reads (the default already does that), but also preventing
non-repeatable reads.
That prevention of non-repeatable reads is a big upside, but holding even shared locks until the end of
the transaction can block users’ access to objects, and therefore hurt productivity. Personally, I prefer to
use other data integrity options (such as a CHECK constraint together with error handling) rather than
this choice, but it remains an available option.
The equivalent optimizer hint for the REPEATABLE READ isolation level is REPEATABLEREAD (these are
the same, only no space).
❑ SERIALIZABLE
SERIALIZABLE is something of the fortress of isolation levels. It prevents all forms of concurrency issues
except for a lost update. Even phantoms are prevented.
When you set your isolation to SERIALIZABLE, you’re saying that any UPDATE, DELETE, or INSERT to
the table or tables used by your transaction must not meet the WHERE clause of any statement in that
transaction. Essentially, if the user was going to do something that your
❑ SNAPSHOT
指定事务中任何语句读取的数据都将是在事务开始时便存在的数据的事务上一致的版本。事务只能识别在其开始之前提交的数据修改。在当前事务中执行的语句将看不到在当前事务开始以后由其他事务所做的数据修改。其效果就好像事务中的语句获得了已提交数据的快照,因为该数据在事务开始时就存在。
http://blog.csdn.net/sandyzhs/archive/2008/10/24/3136465.aspx