Overview of some often used SQL Server TSQL code (2005/2008/2012/2014/2016)
Version : 4.6
Date : 22/06/2014
By : Albert van der Sel
Remark : Please refresh this page to see any updates. It might take a few seconds to fully load this page.
- Type of doc: It's just a simple listing of some often used SQL Server 2005/2008/2012/2014/2016 SQL and TSQL Statements.
- For who : For anyone who likes a simple list of easy examples.
- Best Usage : Just browse around, or use "find/search" to look for a keyword or identifier, or use the menu below.
This document lists some often used TSQL code and statements. It's, ofcourse, not much compared
to extensive documentation like for example "Books Online" or "BOL".
But maybe it's convienient to have some often used code "close together" in an easy to browse document.
Mostly, it shows commands only, with only minimal comment.
Note: In TSQL code, anything behind "--", or between "/* */", is comment.
It might take a few seconds to load this document...
changes with respect to the base version 4.6, from 22/06/2014:
- 29/05/2017 Added section 4, on Performance analyzing.
Main Contents:
Section 1. General Code and Examples:
1.0. CHECK ON THE PROGRESS OF A BACKUP, OR RESTORE, WITH TSQL.
1.1. GET A LISTING OF FILE AND FILEGROUP INFORMATION
1.2. CREATING A DATABASE USING A TSQL SCRIPT, AND ADDING/RENAMING OF FILES.
1.3. CREATING A SQL SERVER LOGIN AND A WINDOWS LOGIN (meaning: creating accounts)
1.4. SOME DICTIONARY QUERIES FOR VIEWING SESSIONS, LOCKS, BLOCKING SESSIONS, OBJECT ACCESS
1.5. SOME DICTIONARY QUERIES FOR VIEWING WAITS
1.6. SOME EXAMPLES OF USING xp_cmdshell
1.7. QUICKLY CREATE A COPY TABLE FROM AN EXISTING TABLE (WITH ALL DATA)
1.8. A FEW NOTES ABOUT "SCRIPTS", AND STORED PROCEDURES AND FUNCTIONS
1.9. CREATING A "LOOP" IN A SCRIPT, OR PROCEDURE, USING A CURSOR
1.10. HOW TO KILL A SESSION
1.11. HOW TO FIND AN OBSTRUCTING, OR BLOCKING, SESSION
1.12. SOME OFTEN USED STANDARD FUNCTIONS
1.13. REMOVING "UNWANTED" CHARACTERS FROM A FIELD OF A TABLE, OR STRING
1.14. REMOVING "UNWANTED" QUOTES FROM A FIELD OF A TABLE, OR STRING
1.15. SIMPLE OVERVIEW MOST IMPORTANT SQL SERVER DATATYPES
1.16. THE CAST AND CONVERT CONVERSION FUNCTIONS
1.17. A FEW WORDS ON GRANTING ROLES AND PERMISSIONS TO LOGINS (users) USING TSQL
1.18. A FEW WORDS ON SHOWING PERMISSIONS USING TSQL
1.19. GET A LISTING OF ALL COLUMNNAMES WITH ALL TABLES, AND DATATYPES, USING TSQL
1.20. A FEW NOTES ABOUT THE "@@" FUNCTIONS
1.21. HOW TO SCRIPT YOUR DATABASE
1.22. HOW TO GENERATE THE INSERT STATEMENTS FOR A TABLE WITH DATA
1.23. OVERVIEW SQL SERVER VERSIONS AND BUILDS v 7 - 2012
1.24. LOGGING FROM STORED PROCEDURES AND TSQL
1.25. USING EXEC IN TSQL CODE
1.26. HOW TO VIEW WHETHER PROCESSES (SPIDS) WERE NTLM OR KERBEROS AUTHENTICATED
1.27. A FEW NOTES ON USER DEFINED FUNCTIONS
1.28. HOW TO GET A LIST OF PK AND FK CONSTRAINTS
1.29. HOW TO IMPLEMENT PARTITIONING
1.30. A FEW NOTES ON DYNAMIC SQL
1.31. A FEW NOTES ON BACKUP AND RESTORE USING TSQL STATEMENTS
1.32. COMPARING TABLES: FIND ROWS WHICH ARE IN ONE TABLE, BUT WHICH ARE NOT IN ANOTHER SIMILAR TABLE.
1.33. A FEW NOTES ON REVIVING SQL ACCOUNTS AFTER MOVE (OR RESTORE) OF A DATABASE
1.34. A FEW NOTES ON INDEX ANALYSIS
1.35. USING PROMPT TOOLS (OR GRAPHICAL TOOLS LIKE SSMS) TO CONNECT TO A NAMED INSTANCE, ON SOME PORT
1.36. SOME REMARKS ON THE SCOM ACCOUNT IN SQL SERVER.
1.37. SOME SYSTEMVIEW QUERIES ON 2005/2008 DATABASE MIRRORING
1.38. SOME SYSTEMVIEW QUERIES ON 2012/2014 "ALWAYSON" CLUSTER
1.39. "ALWAYSON" CLUSTER AND TSQL
1.40. CHANGE A USER THROUGHOUT A SET OF DATABASES
1.41. CREATE A SQL SERVER AGENT PROXY ACCOUNT.
1.42. SOME DBCC MEMORY statements.
1.43. CREATE A SERVER-SIDE TRACE.
1.44. SHOW ACCESSED OBJECTS.
1.45. BCP TABLE DATA EXPORTS OR IMPORTS.
1.46. SQL SERVER AND SSL.
1.47. OPENQUERY() AND OPENROWSET().
1.48. SOME QUERIES TO SEE THE STATE OF THE TRANSACTION LOG.
1.49. THE COLUMNSTORE INDEX (2012/2014/2016).
Section 2. Some further exercises:
2.1. Excercise 1: SIMPLE EXAMPLE OF CODE TO GENERATE THE BASIC "CREATE TABLE" STATEMENTS
2.2. Excercise 2: SIMPLE EXAMPLE OF AN AUTOMATED TSQL BACKUP SCRIPT
Section 3. Old Stuff:
3.1. OLDER txt FILE LISTING DBA SCRIPTS AND STATEMENTS
Section 4. Performance analysis.
4.1. SOME VERY TRIVIAL STUFF YOU MIGHT WANT TO CHECK FIRST (or to keep in mind...).
4.2. SOME SQL SERVER POINTERS TO INVESTIGATE PERFORMANCE.
--Listing of changed database objects (meaning DDL, as of a certain date).
--Listing of Read/Write IO, and "stalls", on the database files.
--Identifying the top "wait" events.
--Listing of Tables and Indexes, types of index, ordered by the number of rows.
--Identifying re-compilations.
--Some DBCC commands related to plans, memory, caches, and how to remove plans.
--Locks and Latches.
Section 1. General code plus examples.
1.0. QUICK CHECK ON THE PROGRESS OF YOUR BACKUP USING TSQL:
If you run a backup job, or if you use the 'BACKUP DATABASE ' or 'BACKUP LOG ' TSQL command, were you never curious
as to what the progress of the backup is, like "%complete" and "estimated time left"?
Well, now you can. The following Query should work on your system. Try this one:
SELECT session_id, status, blocking_session_id, wait_type,
percent_complete, total_elapsed_time, command, estimated_completion_time
FROM sys.dm_exec_requests
WHERE command like '%BACKUP%'
You can use the same statement to see the progress of a RESTORE, or ALTER command, like for example:
SELECT session_id, status, blocking_session_id, wait_type,
percent_complete, total_elapsed_time, command, estimated_completion_time
FROM sys.dm_exec_requests
WHERE command like '%RESTORE%'
1.1. GET A LISTING OF FILE AND FILEGROUP INFORMATION:
Often you need a listing of your database files and characteristics, like paths, sizes etc..
Here are a few queries that will show you that information.
=> 1.1.1 View filenames and sizes of the current database:
-- use Yourdatabase_Name
-- go
-- Overall listing, and names, and free/used space:
SELECT name AS [File Name] , physical_name AS [Physical Name], size/128 AS [Total Size in MB],
size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS [Available Space In MB]
FROM sys.database_files;
-- Selected info from sysfiles:
SELECT sysfiles.fileid, sysfiles.groupid, sysfiles.size,
(sysfiles.size * 8 / 1024) AS "SIZE_IN_MB",
substring(sysfiles.name, 1, 30) AS NAME,
substring(sysfiles.filename, 1, 100) AS FILENAME,
substring(sysfilegroups.groupname, 1, 40) AS GROUPNAME
FROM sysfiles, sysfilegroups
WHERE sysfiles.groupid=sysfilegroups.groupid
=> 1.1.2 View names and sizes from all databases:
SELECT db_name(database_id), Name [Database], Physical_Name [Physical file], size*8/1024 [Size_MB] FROM sys.master_files
exec sp_databases
=> 1.1.3 Very Nice One: View names, sizes, and many other properties from all databases:
SELECT
substring(d.name,1,40) [DBNAME], d.recovery_model_desc,
substring(d.collation_name,1,35) [Collation], substring(d.page_verify_option_desc,1,20) [CheckPage],
substring(m.Physical_Name,1,90) [Physical file], m.size*8/1024 [Size_MB], substring(d.state_desc,1,10) [Status]
FROM sys.databases d, sys.master_files m
WHERE m.database_id=d.database_id
ORDER BY d.name
1.2. CREATING A DATABASE USING A TSQL SCRIPT, AND ADDING/RENAMING FILES:
1.2.1 Example of creating a Database (called "SALES").
CREATE DATABASE SALES
ON
PRIMARY
(NAME=salesPrimary,
FILENAME='C:\mssql\data\sales\sales.mdf',
SIZE=100MB,
MAXSIZE=200,
FILEGROWTH=20),
FILEGROUP salesData
(NAME=salesData01,
FILENAME='D:\mssql\data\sales\salesdata01.ndf',
SIZE=100MB,
MAXSIZE=200,
FILEGROWTH=10),
FILEGROUP salesIndex
(NAME=salesIndex01,
FILENAME='E:\mssql\data\sales\salesindex01.ndf',
SIZE=100MB,
MAXSIZE=200,
FILEGROWTH=10)
LOG ON
(NAME=salesLog01,
FILENAME='F:\mssql\data\sales\saleslog01.ldf',
SIZE=100MB,
MAXSIZE=200,
FILEGROWTH=10)
GO
You can adjust such a script to your own needs.
Notes: for performance and availability reasons:
- The lines in "bold" are the starts of the "filegroup" definitions. The primary filegroup, (with the .mdf file), is supposed
to be reserved for the database dictionary (comparable to the system tablespace in Oracle).
- Try to create seperate filegroups to place the tables and indexes on: don't put them into the "primary" (.mdf) file.
So, the .mdf file is supposed to be reserved for the dictionary, the .ldf file(s) are for the transaction log, and
the ordinary (regular) datafiles (for tables and indexes), uses the .ndf extension.
- Always try to seperate the transactionlog, from the datafiles, on different drives/filesystems.
- Try to create seperate filegroup(s) (containing .ndf files) for holding tables, and indexes.
1.2.2 Example of adding a "FileGroup" and file to a Database.
Suppose, for example, that we did not specified an INDEX filegroup for the CREATE statement of the
SALES database, as shown above.
Now, we can add a seperate filegroup (and file) with the intention to hold the indexes of the database.
ALTER DATABASE SALES
ADD FILEGROUP [INDEX]
GO
ALTER DATABASE SALES
ADD FILE
(
NAME = Sales_Index,
FILENAME = 'E:\MSSQL\DATA\SALES\sales_index.ndf',
SIZE = 1200MB,
MAXSIZE = 1500MB,
FILEGROWTH = 50MB
)
TO FILEGROUP [INDEX]
GO
Note: You can create, or add, multiple filegroups to a database, designated for some purpose.
For example, with the "CREATE object ... ON FILEGROUP filegroup_name" statement for an TABLE, or INDEX, will determine on
which filegroup this object will be put on.
=> Some other examples:
-> Altering the size of a Database file:
ALTER DATABASE SALES
MODIFY FILE
(
NAME = Sales_Index,
size=360000MB
);
GO
ALTER DATABASE msdb
MODIFY FILE
(NAME = MSDBLog,
SIZE = 100MB);
GO
-> Adding a database file to TEMPDB:
TempDB is a "special" Database, in use for sort operations at query runtime, temporary tables, index rebuilds etc..
It does not use exactly the same construction of "filegroups" as with user databases.
It only has the default filegroup, and transaction log files.
ALTER DATABASE TEMPDB
ADD FILE
(
NAME = TempDB_5,
FILENAME = 'T:\SQL_TempDB\TempDB_5.ndf',
SIZE = 8192MB,
MAXSIZE = 16384MB,
FILEGROWTH = 64MB
)
TO FILEGROUP [default]
GO
1.2.3 Renaming Logical and Physical files.
1. Rename logical file names of Database files:
Let's create a test database.
CREATE DATABASE TEST1
ON
PRIMARY
(NAME=Test1Primary,
FILENAME='R:\SQLData\test1.mdf',
SIZE=100MB,
MAXSIZE=200,
FILEGROWTH=20),
FILEGROUP Test1Data
(NAME=Test1Data,
FILENAME='R:\SQLData\test1data.ndf',
SIZE=100MB,
MAXSIZE=200,
FILEGROWTH=10)
LOG ON
(NAME=Test1Log,
FILENAME='S:\SQLlog\test1log.ldf',
SIZE=100MB,
MAXSIZE=200,
FILEGROWTH=10)
GO
Now, let's see what the "logical" and "physical" names are:
SELECT substring(name,1,20) AS [Logical FileName] , substring(physical_name,1,30) AS [PhysicalName], size/128 AS [Total Size],
size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS [Available Space In MB]
FROM sys.database_files;
Logical FileName.....Physical Name..................Total Size.......Available Space In MB
Test1Primary.........R:\SQLData\test1.mdf...........100..............98.625000
Test1Log.............S:\SQLlog\test1log.ldf.........100..............99.570313
Test1Data............R:\SQLData\test1data.ndf.......100..............99.937500
Ok, let's rename, for example, two Logical filenames:
ALTER DATABASE [TEST1]
MODIFY FILE (NAME=N'Test1Primary', NEWNAME=N'Primary')
GO
ALTER DATABASE [TEST1]
MODIFY FILE (NAME=N'Test1Log', NEWNAME=N'Tlog')
GO
Logical FileName.....Physical Name..................Total Size.......Available Space In MB
Primary..............R:\SQLData\test1.mdf...........100..............98.625000
TLog.................S:\SQLlog\test1log.ldf.........100..............99.570313
Test1Data............R:\SQLData\test1data.ndf.......100..............99.937500
2. Rename the physical file names of Database files:
First, put the database in single user mode
Then detach the database. After that, using explorer or the command prompt etc.., rename the physical files.
ALTER DATABASE TEST1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
USE [master]
GO
EXEC master.dbo.sp_detach_db @dbname = N'TEST1'
GO
Now, use whatever tool to rename or move the phyiscal files.
Attach the database again:
USE [master]
GO
CREATE DATABASE Test1 ON
( FILENAME = N'R:\SQLDATA\test1test.mdf' ),
( FILENAME = N'R:\SQLDATA\test1datatest.ndf' ),
( FILENAME = N'S:\SQLLog\test1log.ldf' )
FOR ATTACH
GO
Put database in multi user mode again:
ALTER DATABASE TEST1 SET MULTI_USER
GO
1.2.3 Some related Systemviews / Dictionary views
:
=> For viewing database characteristics:
select * from sys.databases
select name, database_id, create_date, user_access_desc, state_desc from sys.databases
=> For viewing Filegroup and file information:
-- Go to the database you are interrested in:
-- USE DATABASENAME -- e.g.: use sales
-- GO
Get all file information:
SELECT * FROM sys.sysfiles
SELECT * FROM sys.database_files
Overall listing, and names and free/used space:
SELECT name AS [File Name] , physical_name AS [Physical Name], size/128 AS [Total Size in MB],
size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS [Available Space In MB]
FROM sys.database_files;
Selected info from sysfiles:
SELECT sysfiles.fileid, sysfiles.groupid, sysfiles.size,
(sysfiles.size * 8 / 1024) AS "SIZE_IN_MB",
substring(sysfiles.name, 1, 30) AS NAME,
substring(sysfiles.filename, 1, 100) AS FILENAME,
substring(sysfilegroups.groupname, 1, 40) AS GROUPNAME
FROM sysfiles, sysfilegroups
WHERE sysfiles.groupid=sysfilegroups.groupid
1.3. CREATE A "SQL SERVER" LOGIN AND A "WINDOWS" LOGIN:
There are two main types of logins (accounts):
- A "Windows" login (authenticated by Windows, either local, or by AD)
- A (internal) SQL login (defined in-, and authenticated by SQL Server)
(and indeed a few other special type of accounts are possible.)
1.3.1 Create an (internal) SQL Server account (also called "login"):
This is an internal SQL Server account, independent from Windows authentication.
Anyone who knows this account and password, can logon to SQL server itself,
and it does not matter on what type of machine or Domain (if applicable) the user is working from,
as longs as the user has the client software and all neccesary networking software.
So, the account that the real user used to logon to the OS, has nothing to do with the "SQL Server account".
CREATE LOGIN login_name WITH PASSWORD=password;
Example:
CREATE LOGIN webuser WITH PASSWORD='beerisok!'
Most important options/clauses (there are more):
MUST_CHANGE (must change password at first logon)
DEFAULT_DATABASE = database
DEFAULT_LANGUAGE = language
CHECK_EXPIRATION = { ON | OFF}
CHECK_POLICY = { ON | OFF}
Example:
CREATE LOGIN webuser WITH PASSWORD='welcome' MUST_CHANGE, CHECK_EXPIRATION=on
Example:
CREATE LOGIN mary WITH PASSWORD='welcome01', DEFAULT_DATABASE=TEST
(Some options need to be used in combination with others.)
Example: Creating a SQL Login, and grant access to the TEST database, and grant the login some common roles:
CREATE LOGIN mary WITH PASSWORD='welcome01', DEFAULT_DATABASE=TEST
GO
USE TEST
GO
EXEC sp_grantdbaccess 'mary', 'mary'
GO
EXEC sp_addrolemember 'db_datareader', 'mary'
GO
EXEC sp_addrolemember 'db_datawriter', 'mary'
GO
1.3.2 Create a Windows login:
This time, a user which has been authenticated by Windows (OS logon or Domain logon),
needs only be sort of "defined" in SQL Server. The authentication was already done by Windows, so
in creating the account, you do not specify a password.
In much literature, you see terms like "Windows authentication Mode", or "Trusted connection".
In most cases, we are talking about Domain accounts, like "Domain_name\User_name".
But also local accounts (of a Server, or other machine) can be added in the same way.
CREATE LOGIN [Domain\user | Localmachine\user] FROM WINDOWS;
Examples:
CREATE LOGIN [ABCCORP\john] FROM WINDOWS;
CREATE LOGIN [SERVER1\mary] FROM WINDOWS;
Creating logins does not mean those accounts have any permissions in SQL Server, unless you explicitly grant roles or permissions.
-Usually, you grant "roles" to accounts, by which the useraccounts inherit certain permissions.
-Only in special cases, you grant permissions to individual logins directly.
Roles in SQL Server have quite the same functionality as "roles" in other database systems like Oracle.
Accounts and roles are also sometimes called "principals".
Note:
About granting "roles" and other permissions to logins: please see Section 1.17.
1.3.3 Using some special clauses:
In the CREATE LOGIN statement (new account), or the ALTER LOGIN statement (existing account), the following options can be used:
CHECK_POLICY = { ON | OFF }
CHECK_EXPIRATION = { ON | OFF }
Using the graphical SQL Server Management Studio (SSMS), makes it easy to create both types of logins.
- Note that with a Windows Login, the "enforce password expiration" and "enforce password expiration" are grayed out.
- With a SQL Login, you can check or uncheck those options.
So, per default, Windows Logins inherit the password policy as is defined in the security mechanism of the Operating System,
thus either the Local Machine, or AD.
For a SQL account, you can set or unset those options. If you set it, it inherits the password policy- and expiration
as is defined in the OS.
When using TSQL, you can achieve the same effect as in the graphical SSMS utility.
In the CREATE LOGIN statement (new account), or the ALTER LOGIN statement (existing account), just use the following options:
CHECK_EXPIRATION = { ON | OFF }
Applies only to SQL Server logins.
It specifies whether password expiration policy should be enforced on this login.
CHECK_POLICY = { ON | OFF }
Applies only to SQL Server logins.
It specifies that the Windows password policies of the computer on which SQL Server is running should be enforced on this login.
1.4. SOME DICTIONARY QUERIES FOR VIEWING SESSIONS, LOCKS, BLOCKING SESSIONS, OBJECT ACCESS:
1.4.1 VIEWING SESSIONS:
SELECT session_id,
substring(convert(varchar(20),login_time),1,17) AS LOGINTIME,
substring(host_name,1,10) AS HOSTNAME,
substring(program_name,1,30) AS PROGRAM,
substring(login_name,1,10) AS LOGINNAME,
substring(nt_user_name,1,10) AS NT_USER,
substring(status,1,10) AS STATUS,
lock_timeout,
row_count
FROM sys.dm_exec_sessions
SELECT spid, cpu, physical_io, blocked, cmd, waittime,
substring(convert(varchar(20),last_batch), 1, 20) as "LASTBATCH",
substring(nt_username, 1, 15) AS "USERNAME",
substring(loginame, 1, 20) AS "LOGINNAME",
substring(hostname, 1, 15) AS "HOSTNAME",
DB_NAME(dbid) AS "DB",
substring(program_name, 1, 40) AS "PROGRAM"
FROM master.dbo.sysprocesses
Note the field "blocked" in the above query, which also allows you to easily identify blocked sessions.
SELECT spid, cpu, physical_io, blocked, cmd, waittime,
substring(convert(varchar(20),last_batch), 1, 20) as "LASTBATCH",
substring(nt_username, 1, 15) AS "USERNAME",
substring(loginame, 1, 20) AS "LOGINNAME",
substring(hostname, 1, 15) AS "HOSTNAME",
DB_NAME(dbid) AS "DB",
substring(program_name, 1, 40) AS "PROGRAM"
FROM master.dbo.sysprocesses
WHERE cmd not like 'AWAIT%' -- show only active statements.
-- AND loginame <> 'sa'
SELECT distinct
SUBSTRING(DB_NAME(dbid),1,30) as "DATABASE",
substring(hostname, 1, 15) AS "HOSTNAME",
substring(loginame, 1, 20) AS "LOGINNAME",
substring(program_name, 1, 40) AS "PROGRAM",
db_name(dbid) as "Database"
FROM master.dbo.sysprocesses
where loginame not in ('sa')
and DB_NAME(dbid) not in ('master','msdb')
order by DB_NAME(dbid)
1.4.2 VIEWING LOCKS AND BLOCKING SESSIONS:
exec sp_lock
SELECT
s.login_name, s.nt_user_name,
r.session_id AS BLOCKED_SESSION_ID,
r.blocking_session_id AS BLOCKING_SESSION_ID,
s.program_name,
r.start_time,r.status,r.command,database_id,
r.wait_type,r.open_transaction_count,r.percent_complete,r.cpu_time,r.reads,r.writes,r.deadlock_priority
from sys.dm_exec_sessions s, sys.dm_exec_requests r
where s.session_id=r.session_id AND blocking_session_id > 0
SELECT a.session_id AS blocked_session_id, b.session_id AS blocking_session_id, c.text AS blocking_text,
e.wait_type AS blocking_resource,
e.wait_duration_ms, d.text AS blocked_text FROM sys.dm_exec_requests a INNER JOIN sys.dm_exec_requests b
ON a.blocking_session_id = b.session_id CROSS APPLY sys.dm_exec_sql_text(b.sql_handle) c CROSS APPLY sys.dm_exec_sql_text(a.sql_handle) d
INNER JOIN sys.dm_os_waiting_tasks e ON e.session_id = b.session_id
SELECT
L.request_session_id AS SPID,
DB_NAME(L.resource_database_id) AS DatabaseName,
O.Name AS LockedObjectName,
P.object_id AS LockedObjectId,
L.resource_type AS LockedResource,
L.request_mode AS LockType,
ST.text AS SqlStatementText,
ES.login_name AS LoginName,
ES.host_name AS HostName,
TST.is_user_transaction as IsUserTransaction,
AT.name as TransactionName,
CN.auth_scheme as AuthenticationMethod
FROM sys.dm_tran_locks L
JOIN sys.partitions P ON P.hobt_id = L.resource_associated_entity_id
JOIN sys.objects O ON O.object_id = P.object_id
JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id
JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id
JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id
JOIN sys.dm_exec_connections CN ON CN.session_id = ES.session_id
CROSS APPLY sys.dm_exec_sql_text(CN.most_recent_sql_handle) AS ST
WHERE resource_database_id = db_id()
ORDER BY L.request_session_id
1.4.3 COUNTING LOCKS IN A DATABASE, SHOWING TYPE OF LOCKS:
SELECT request_session_id, request_type, request_mode FROM sys.dm_tran_locks where resource_database_id="dbid" -- fill in dbid
or (same type of query)
SELECT request_session_id, request_type, request_mode FROM sys.dm_tran_locks where DB_NAME(resource_database_id)='name' -- fill the database name
-- list the "harder"locks (exclusive row etc..:
SELECT COUNT(*) FROM sys.dm_tran_locks where resource_database_id=5 -- fill in dbid
and request_mode in ('IX','SIX','X','Sch-M','U','RangeX-X','IU','SIU')
SELECT COUNT(*) FROM sys.dm_tran_locks where db_name(resource_database_id)='name' -- fill in name
and request_mode in ('IX','SIX','X','Sch-M','U','RangeX-X','IU','SIU')
SELECT distinct s.login_name, s.nt_user_name, r.request_session_id, r.request_type, r.request_mode FROM sys.dm_tran_locks r, sys.dm_exec_sessions s
WHERE s.session_id=r.request_session_id
and r.request_mode in ('IX','SIX','X','Sch-M','U','RangeX-X','IU','SIU')
select db_name(resource_database_id), request_type, request_mode, request_session_id from sys.dm_tran_locks
where request_mode not in ('S')
1.4.4 ATTEMPTING TO REINDEX IN STANDARD EDITION (no good lock management while reindex):
-- Just a try:
set nocount on
DECLARE @TableName varchar(255)
DECLARE @AllowPLock INT
DECLARE @LOCKS INT
DECLARE MyCursor CURSOR FOR
SELECT DISTINCT t.table_name, s.allow_page_locks FROM information_schema.tables t, sys.indexes s
WHERE t.table_type = 'base table' AND t.TABLE_NAME=OBJECT_NAME(s.object_id) AND s.allow_page_locks=1
AND t.table_name not like 'ROS_SHI%'
AND t.table_name not like 'C%'
SELECT @LOCKS=(SELECT COUNT(*) FROM sys.dm_tran_locks where resource_database_id=38 --Choose your DBID
and request_mode in ('IX','SIX','X','Sch-M','U','RangeX-X','IU','SIU'))
IF @LOCKS=0
BEGIN
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO @TableName, @AllowPLock
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @LOCKS=(SELECT COUNT(*) FROM sys.dm_tran_locks where resource_database_id=38 --Choose your DBID
and request_mode in ('IX','SIX','X','Sch-M','U','RangeX-X','IU','SIU'))
IF @LOCKS=0
BEGIN
EXEC ('DBCC DBREINDEX('+@TableName+','+''''',80)')
END
FETCH NEXT FROM MYCursor INTO @TableName, @AllowPLock
END
CLOSE MyCursor
DEALLOCATE MyCursor
END
1.4.5 VIEWING TABLE ACCESS WITH LAST UPDATE OF TABLE, LAST SCAN:
Fill in the variable @TABLENAME, with your tablename, and execute the script below:
DECLARE @TABLENAME VARCHAR(128)
SET @TABLENAME='fill in your table name'
SELECT DB_NAME(ius.[database_id]) AS [Database],
OBJECT_NAME(ius.[object_id]) AS [TableName],
MAX(ius.[last_user_scan]) AS [last_user_scan_or_last_access],
MAX(ius.[last_user_seek]) AS [last_user_seek],
MAX(ius.[last_user_update]) AS [last_user_update]
FROM sys.dm_db_index_usage_stats AS ius
WHERE ius.[database_id] = DB_ID()
AND ius.[object_id] = OBJECT_ID(@TABLENAME)
GROUP BY ius.[database_id], ius.[object_id];
SELECT [TableName] = name, create_date, modify_date
FROM sys.tables
WHERE name = @TABLENAME
1.5. SOME DICTIONARY QUERIES FOR VIEWING WAITS:
1.5.1 View IO statistics on all database files note: (dbid, fileid)=(null,null):
SELECT * FROM fn_virtualfilestats(null, null)
1.5.2 View Pending IO (disk & network)
SELECT SUM(pending_disk_io_count) AS [Number of pending I/Os] FROM sys.dm_os_schedulers
SELECT * FROM sys.dm_io_pending_io_requests
1.5.3 View IO statistics on all database files of a specific database (dbid, fileid)=(dbid,null):
-- e.g. like dbid=5:
SELECT * FROM fn_virtualfilestats(5, null)
1.5.4 View the top 10 wait statistics:
-- All
select top 10 *
from sys.dm_os_wait_stats
order by wait_time_ms desc
1.5.5 View the top 10 wait statistics, except for well known wait_types due to system processes
select top 10 *
from sys.dm_os_wait_stats
where wait_type not in
(
'KSOURCE_WAKEUP', 'SLEEP_BPOOL_FLUSH', 'BROKER_TASK_STOP',
'XE_TIMER_EVENT', 'XE_DISPATCHER_WAIT', 'FT_IFTS_SCHEDULER_IDLE_WAIT',
'SQLTRACE_BUFFER_FLUSH', 'CLR_AUTO_EVENT', 'BROKER_EVENTHANDLER',
'LAZYWRITER_SLEEP', 'BAD_PAGE_PROCESS', 'BROKER_TRANSMITTER',
'CHECKPOINT_QUEUE', 'DBMIRROR_EVENTS_QUEUE', 'LAZYWRITER_SLEEP',
'ONDEMAND_TASK_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'LOGMGR_QUEUE',
'SLEEP_TASK', 'SQLTRACE_BUFFER_FLUSH', 'CLR_MANUAL_EVENT',
'BROKER_RECEIVE_WAITFOR', 'PREEMPTIVE_OS_GETPROCADDRESS',
'PREEMPTIVE_OS_AUTHENTICATIONOPS', 'BROKER_TO_FLUSH'
)
order by wait_time_ms desc
Much better statement can be found at Randall's blog, together with a nice explanation of wait types.
You can find that here.
1.5.6 View Signal Waits (cpu) and Resource Waits (other waits like IO, locks etc..):
SELECT signal_wait_time_ms=sum(signal_wait_time_ms),'%signal (cpu) waits' = cast(100.0 * sum(signal_wait_time_ms) / sum (wait_time_ms) as numeric(20,2)),
resource_wait_time_ms=sum(wait_time_ms - signal_wait_time_ms),'%resource waits'= cast(100.0 * sum(wait_time_ms - signal_wait_time_ms) / sum (wait_time_ms) as numeric(20,2))
FROM sys.dm_os_wait_stats
1.5.7 Most cpu consuming statements:
select top 10
object_name(st.objectid), db_name(st.dbid), total_worker_time/execution_count AS AverageCPUTime,
CASE statement_end_offset
WHEN -1 THEN st.text
ELSE SUBSTRING(st.text,statement_start_offset/2,statement_end_offset/2)
END AS StatementText
from
sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY AverageCPUTime DESC
1.5.8 sessions and resources (1):
select session_id, host_name, login_name, cpu_time, memory_usage, reads, writes
from sys.dm_exec_sessions order by reads asc
1.5.9 sessions and resources (2):
select e.session_id, e.host_name, e.login_name, e.cpu_time, e.memory_usage, e.reads, e.writes,
db_name(r.database_id), r.wait_type, r.wait_time, r.open_transaction_count
from sys.dm_exec_sessions e, sys.dm_exec_requests r
where e.session_id=r.session_id order by db_name(r.database_id)
1.5.10 Top Queries and stored procedures that have been recompiled (plan_generation_num):
select top 20
sql_text.text, sql_handle, plan_generation_num, execution_count,
db_name(dbid), object_name(objectid)
from
sys.dm_exec_query_stats a
cross apply sys.dm_exec_sql_text(sql_handle) as sql_text
where
plan_generation_num >1
order by plan_generation_num desc
1.5.11 Get an idea of available memory in SQL Server:
If you do this:
select name, type_desc
from sys.all_objects where name like '%memory%'
You will find all DM views which can give you information on memory usage of SQL Server.
If you do this:
select name, type_desc
from sys.all_objects
where name like '%_os_%' and type_desc='VIEW'
You get a lot of views back which you can query on general performance information, but not only of SQL, but about
the OS and the Host as well.
At least, the following query is quite interesting for obtaining a general impression on the amount of available memory:
select total_physical_memory_kb, available_physical_memory_kb,
system_memory_state_desc
from sys.dm_os_sys_memory
In the last column, you may find values like:
"Available physical memory is high"
"Available physical memory is low"
and a few other possible values, which speaks for themselves.
By the way, in section 1.42, you will find some information on a few "DBCC MEMORY" statements.
1.6. SOME EXAMPLES USING xp_cmdshell:
With the (external) stored procedure "xp_cmdshell", you can call DOS (cmd) commands and execute OS shell scripts,
from your SQL code.
To see if it works on your system, try the following from a Query Window:
exec xp_cmdshell 'dir c:\'
This should provide a listing of files and directories in the root of C:\
If instead you see this returned:
Msg 15281, Level 16, State 1, Procedure xp_cmdshell, Line 1
SQL Server blocked access to procedure 'sys.xp_cmdshell' of component 'xp_cmdshell' because
this component is turned off as part of the security configuration for this server.
A system administrator can enable the use of 'xp_cmdshell' by using sp_configure.
For more information about enabling 'xp_cmdshell', see "Surface Area Configuration" in SQL Server Books Online.
Then you must 'activate' it, using either "sp_configure" or using the graphical "Surface Area Configuration" utility.
Warning: many people working in security related fields, advise not to enable this procedure
due to possible risks involved.
1.6.1 Enabling "xp_cmdshell" using "sp_configure":
Copy and paste the following lines of code into a Query Window. Then highlight and execute it.
EXEC master.dbo.sp_configure 'show advanced options', 1
RECONFIGURE
EXEC master.dbo.sp_configure 'xp_cmdshell', 1
RECONFIGURE
To find out if it works now, try again the following from a Query Window:
exec xp_cmdshell 'dir c:\'
1.6.2 Some examples using "xp_cmdshell":
Example 1:
EXEC master.dbo.xp_cmdshell'c:\test\mybatch.cmd'
Example 2:
SELECT @LOGSTRING=@ART_NR+' :'+@ACTION+' '+CONVERT(VARCHAR(64),@ACTION_DATE)
SELECT @log_cmd='echo'+' '+@LOGSTRING+' >> C:\TEMP\LOAD_FILE.LOG'
EXEC master.dbo.xp_cmdshell @log_cmd
Example 3:
SELECT @totalcommAND='bcp ##BCP_LOAD in '+@importpath+' -c -F2 -T'
EXEC @RESULT = master.dbo.xp_cmdshell @totalcommAND
IF (@RESULT <> 0)
BEGIN
SET @MESSAGE='Error loading data in temporary table. Possibly wrong path or file not found.'
GOTO error_section
END
Example 4:
DECLARE @cmd sysname, @var sysname
SET @var = 'Hello world'
SET @cmd = 'echo ' + @var + ' > var_out.txt'
EXEC master..xp_cmdshell @cmd
DECLARE @cmd sysname, @var sysname
SET @var = 'dir/p'
SET @cmd = @var + ' > dir_out.txt'
EXEC master..xp_cmdshell @cmd
Example 5:
DECLARE @FileName varchar(50),
@bcpCommand varchar(256)
SET @FileName = REPLACE('c:\authors_'+CONVERT(char(8),GETDATE(),1)+'.txt','/','-')
SET @bcpCommand = 'bcp "SELECT * FROM pubs..authors ORDER BY au_lname" queryout "'
SET @bcpCommand = @bcpCommand + @FileName + '" -U albert -P password -c'
EXEC master..xp_cmdshell @bcpCommand
1.7. QUICKLY CREATE A COPY TABLE FROM AN EXISTING TABLE (WITH ALL DATA):
Suppose you have a table X with datarows.
If you want to create a copy table, with the same columns, datatypes, and all datarows, you can issue
a statement like the example below. Note that the copy table Y should NOT exist beforehand.
Table Y will be created "on the fly".
Example:
select * into Y
from X
This statement will create table Y with the same columns, datatypes, and the same rows, from table X.
But table Y will not have similar "constraints" (like Primary Key, Foreign Keys) like table X has.
You will only have a duplicate table Y, with the same rows, as table X has.
Note:
The upper SQL statement is the SQL Server equivalent of the famous Oracle CTAS statement (Create Table As Select).
Note: as an alternative:
You can easily script table "X", for example, using SSMS. Then when you have the script, change the name of the table to
another appropriate name, like "Y" (in this example we ignore any constraints in the script like a possible primary key name).
Next, create table Y (execute the script).
Next, use the SQL statement:
INSERT INTO Y
SELECT * FROM X
1.8. A FEW NOTES ABOUT "SCRIPTS", AND STORED PROCEDURES AND FUNCTIONS:
If you want to run TSQL code, there are several ways to interact to SQL Server, for example:
- Starting a graphical "query window" from SSMS (the graphical SQL Server Management Studio.
- From a Operating System prompt, starting a client like "sqlcmd" or "osql".
- Other tools/clients using OLEDB or ODBC etc.. to send TSQL and receive resultsets.
Using the graphical "Query Window" is probably the most "popular" method.
If you are not so familiar with the query window, then just start SMSS and logon to SQL Server.
It should not be too difficult to create a "query window", by browsing the various buttons and menu's:
In the upper left corner of SSMS, you should find a "New Query" control.
Globally speaking, you can create two "types" of plain TSQL code:
- Or you just type in TSQL code in the Query Window, and when done, execute it. Ofcourse, you can also
write that code in any editor (like notepad, or notepad+ etc..), save it as a file anywhere on the filesystem,
and when needed, you can copy/paste it in the Query Window and run the script.
In this method, you are just building scripts (sometimes also called TSQL batches).
- You can also write "stored procedures" and "functions". These have a sort of defined format, but the body
of the code can be anything you want that procedure or function to do. This time, you always start out
with "CREATE PROCEDURE procedure_name AS..." and then follows the body of your code.
Big difference with just scripts is, that procedures and functions are stored in the database, and you can
grant permissions on them, as well as there are other levels of control.
Also, objects like procedures are pre-compiled and parsed, and generally speaking, execute fast.
Whether you write scripts, or build procedures, you have the full power of TSQL available, like:
IF .. THEN .. ELSE.. statements, WHILE loops, CASE statements, and all other fabulous logic that's build into TSQL.
1.8.1 A few examples of Scripts:
Example:
Suppose you have a database for just playing around.
Suppose you have (or create) these PRODUCTS and DAYORDERS tables:
CREATE TABLE [dbo].[Products]
(
Product_id INT NOT NULL,
Product_Name VARCHAR(20) NOT NULL,
Unit_price DECIMAL(7,2) NOT NULL,
No_In_Stock INT NOT NULL,
Must_Order CHAR(1) NOT NULL, -- we use it as a boolean field: y or n
)
GO
CREATE TABLE [dbo].[DayOrders]
(
Order_id int NOT NULL,
Product_id int NOT NULL,
Quantity_Sold int NOT NULL,
)
GO
Note: often, as the schema who owns the tables, the database owner (dbo) is used.
Let insert a few records for demonstration purposes:
insert into products values (1,'bicycle',200.00,5,'N')
insert into products values (2,'motoroil',5.75,7,'N')
insert into products values (3,'hammer',10.00,4,'N')
insert into products values (4,'roadmap',3.00,10,'N')
insert into DayOrders values (177,1,4)
insert into DayOrders values (178,4,20)
insert into DayOrders values (179,3,12)
Here we have only a few records per table, but let's imagine, that here we are dealing with thousends of records...
The DayOrders table collects what is sold today. But, is the invetory in the products table still sufficient,
or should we order new products? Let's say that when (Quantity_Sold - No_In_Stock) > 0, then we must order
new items for that particular product. Let's create a script to determine what is the status:
DECLARE @ProductId INT
DECLARE @MaxProductId INT
DECLARE @PRODUCT_NAME VARCHAR(20)
DECLARE @NO_IN_STOCK INT
DECLARE @QUANTITY_SOLD INT
SET @ProductId=1
SET @MaxProductId=(SELECT MAX(Product_id) from PRODUCTS)
WHILE @ProductId <= @MaxProductId
BEGIN
  IF exists (select Product_Id from DayOrders where Product_id=@ProductId)
  SET @NO_IN_STOCK=(SELECT NO_IN_STOCK FROM PRODUCTS WHERE Product_id=@ProductId)
  SET @QUANTITY_SOLD=(SELECT QUANTITY_SOLD FROM DAYORDERS WHERE Product_id=@ProductId)
  SET @PRODUCT_NAME=(SELECT PRODUCT_NAME FROM PRODUCTS WHERE Product_id=@ProductId)
  IF (@QUANTITY_SOLD-@NO_IN_STOCK) > 0
    BEGIN
    PRINT 'FOR PRODUCT '+@PRODUCT_NAME+' WE MUST ORDER '+convert(varchar(10),@QUANTITY_SOLD-@NO_IN_STOCK)+' ITEMS'
    END
SELECT @ProductId=@ProductId+1
END
The result the script produces is:
FOR PRODUCT hammer WE MUST ORDER 8 ITEMS
FOR PRODUCT roadmap WE MUST ORDER 10 ITEMS
By the way, if you have typed in, or through copy/paste, or via another method, loaded a script, if you "highlight" the code
with your mouse, after that you can execute it using the "Execute" button in the SSMS menu.
Or you can use the Ctrl-E keycombination.
Typically, a script starts with a variable declaration like "DECLARE @ProductId INT", where a variablename
starts with the "@" character. Also, you must tell SQL Server what the datatype of that variable is.
Next, in this script, we assign two variables a certain value, like "SET @ProductId=1", which sets the beginmarker
of the loop we will define a few lines down.
The endmarker of the loop, is the Maximum of the product_id, so that we can be sure to parse through all product_id's.
Next, we actually start the loop, using the "WHILE expression=true" construct.
In this case, we want the loop to cycle using "WHILE @ProductId <= @MaxProductId"
The main purpose is, just to show you how such a "block of TSQL code" usually "looks" like.
Please take notice of the logic like:
WHILE (some condition is true)
BEGIN
  Some statements
END
And also take notice of logic like:
IF (some condition is true)
BEGIN
  Some statements
END
In most programmatic "languages", the IF syntax is like "IF condition THEN some_statements ELSE other_statements".
But in TSQL, we do not have the "THEN". What we can use is (the ELSE branch is optional, and use it if neccessary):
IF (some condition is true)
  BEGIN
    Some statements
  END
ELSE
  BEGIN
    Some other statements
  END
Also the "BEGIN" and "END" enclosures, are often just optional. You may use them to make code easier to read, but
in many constructs (like the IF), it's totally optional.
A few other examples of blocks of TSQL code:
Here are a few other examples to browse through:
-- Take a look at this block of TSQL code:
DECLARE @I INT
SET @I=1
WHILE @I<5
BEGIN
  PRINT 'My boss bumped his head '+convert(varchar(8),@I)+' times, hehe!'
SELECT @I=@I+1 -- needed to increment the "counter" of the loop
END
Output:
My boss bumped his head 1 times, hehe!
My boss bumped his head 2 times, hehe!
My boss bumped his head 3 times, hehe!
My boss bumped his head 4 times, hehe!
Question: Why do we see 4 records in the result, and not 5?
-- Take a look at this block of TSQL code:
DECLARE @EMP_NAME VARCHAR(64)
SELECT @EMP_NAME=(SELECT emp_name FROM EMPLOYEES WHERE emp_id=2)
IF @EMP_NAME='HARRY'
  PRINT 'WE FOUND HARRY'
ELSE
  PRINT 'IT IS NOT HARRY'
So depending on the emp_name where emp_id=2, we can find 'HARRY' or possibly another name.
Note:
By the way, hopefully it's clear, that after you have "declared" a variable, and after you have assigned a value
to that variable, then if you "select" that variable again, the value will be printed (or shown).
For example:
DECLARE @x VARCHAR(32) -- declaration
SELECT @x='abc123'-- assignment of a value
SELECT @x -- show the value
Then the 'second' SELECT will show you the value of @x. You may also use the PRINT statement to show the value.
1.8.2 A few simple examples of creating Stored Procedures:
Contrary to just TSQL scripts (or blocks of TSQL code), a Stored Procedure, is really stored in the Database!
This makes "control", in various ways, a lot better, and it can be used by any (remote) client if needed.
Because it's "inside" the database, any user or program (if permitted) can use it.
You should create a stored procedure, in general terms, like so:
CREATE PROCEDURE procedure_name [optional parameter list]
AS
YOUR STATEMENTS
A few simple examples to get the general idea:
-- Take a look at this code:
A simple procedure that gets a record from a table, based on a parameter "@cust_id"
CREATE PROCEDURE stp_GetCustomerInfo @cust_id int
AS
SELECT Cust_name, city, country
FROM CUSTOMERS
WHERE Cust_id=@cust_id
-- Take a look at this code:
CREATE Procedure dbo.stp_insertEmployee
@emp_id INT, -- a number of input variables, matching the fields of the target table
@emp_name VARCHAR(20),
@salary DECIMAL(7,2),
@hiredate DATETIME,
@gender CHAR(1)
AS
BEGIN
  INSERT INTO EMPLOYEE
  VALUES
  (@emp_id,@emp_name,@salary,@hiredate,@gender)
END
Note: If you created a stored procedure, and if you want to run it from a query tool,
use the syntax:
exec procedure_name
So, in the above examples, you would use:
exec stp_GetCustomerInfo 233
where we used for example 233 as a cust_id.
exec stp_insertEmployee 1,'harry',2000,'2011/1/1','M'
Where we want to insert a record defined with the values "1,'harry',2000,'2011/1/1','M'"
If you want to execute a stored procedure which does not have any parameter, just use:
exec procedure_name
More on Parameters:
Suppose we create these two simple procedures, which will just print their input variables.
CREATE procedure usp_proc1 @input1 varchar(32)
AS
BEGIN
PRINT @input1
END
GO
CREATE procedure usp_proc2 @input2 varchar(32)
AS
BEGIN
PRINT @input2
END
GO
Now let's create a Main routine, that calls both sp's:
CREATE procedure usp_Main
@inp1 varchar(32),
@inp2 varchar(32)
AS
BEGIN
exec usp_proc1 @inp1
exec usp_proc3 @inp2
END
GO
Now, you would for example execute usp_main like so:
exec usp_Main 'Hi', 'Reader'
which would return:
Hi
Reader
You could also create the procedure usp_Main, with two inputvariables, which if they are not set,
will revert to "default" values. In the example below, the defaults are @inp1='Hi' and @inp2='Reader'.
CREATE procedure usp_Main
@inp1 varchar(32)='Hi',
@inp2 varchar(32)='Reader'
AS
BEGIN
exec usp_proc1 @inp1
exec usp_proc3 @inp2
END
GO
In this case, you could execute the procedure as simply as:
exec usp_Main
Ofcourse, you can also call the procedure with the two inputvariables as well, thereby not
using the defaults.
1.8.3 Table variables:
Many programmatic/scripting languages, use the concepts of arrays, or records (or whatever they are called),
as "placeholders" for multiple values.
In TSQL you can declare a variable as a Table datatype. This is really great, since this variable
looks and feels and behaves like a real table.
Here is a simple example:
DECLARE @TABMETA TABLE
(
OWNER VARCHAR(128),
TABLE_NAME VARCHAR(128)
)
INSERT INTO @TABMETA
SELECT TABLE_SCHEMA, TABLE_NAME from INFORMATION_SCHEMA.TABLES
SELECT * FROM @TABMETA
This ofcourse has countless applications.
This section not even scratched "the surface" of creating TSQL code. But if you would read the other
sections below, the topic will be extended somewhat.
1.9. CREATING A SIMPLE LOOP IN A SCRIPT, OR STORED PROCEDURE, USING A CURSOR:
There are several ways to create a "loop" in your scripts (or stored procedures), using TSQL constructs.
One of them, is using a socalled "CURSOR".
You may view a cursor as some "virtual table", which you use in your loop, starting at the "top" of the "table",
and while cycling the loop, all the way down to the "bottom" of the virtual table, where the loop finishes.
As will be explained below, generally, a cursor is not the optimal construct if you will have large datasets.
But for "administrative-like" code, usually it's OK.
Example 1:
Take a look at the following example. Suppose I want to rebuild the indexes of all tables in a database.
As such, I want to "dynamically" generate all neccessary DBCC DBREINDEX statements for all tables.
In SQL Server 2005/2008, the dictionary view "information_schema.tables", contains all table names,
as well as other information.
So, if I define (or declare) a cursor (the virtual table in memory) as:
DECLARE MyCursor CURSOR FOR
SELECT table_name FROM information_schema.tables
Then, after "opening" the cursor (declaring only, is not enough), I will have that virtual table which holds
all tablenames. This I can use for my loop.
To get the first tablename from the cursor, can be done using "FETCH NEXT.."
When you start grabbing from the cursor, the "FETCH NEXT.." will actually be the first value.
Then you start the "WHILE" loop, which will only end if the @@FETCH_STATUS is not equal "0".
So, "WHILE @@FETCH_STATUS = 0", means "we keep looping as long as the @@FETCH_STATUS remains 0.
Then, when at last, when the "FETCH_STATUS <> 0", we have took the last value from the cursor, and the loop must stop.
Take a close look at this code:
set nocount on
DECLARE @TableName varchar(255)
DECLARE MyCursor CURSOR FOR
  SELECT table_name FROM information_schema.tables
  WHERE table_type = 'base table'
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO @TableName
WHILE @@FETCH_STATUS = 0
BEGIN
  SELECT 'DBCC DBREINDEX('+@TableName+','+''''',90)'
  FETCH NEXT FROM MYCursor INTO @TableName
END
CLOSE MyCursor
DEALLOCATE MyCursor
You should always CLOSE and DEALLOCATE your cursor, in order to "clear" it from memory.
Example 2:
Suppose you have an EMPLOYEE table, containing for all employees, the emp_id, emp_name and emp_salary.
You need to fill a new table "EMP_BONUS", and store in that table the emp_id, and emp_bonus.
The emp_bonus is calculated using "emp_salary . 0.1"
Actually, this can easily be done using a relatively simple SQL statement, but I just want to demonstrate
how to solve this using a cursor.
So, you could use the following script:
DECLARE @EMPID INT
DECLARE @SALARY DECIMAL(7,2)
DECLARE @BONUS DECIMAL(7,2)
DECLARE MyCursor CURSOR FOR
  SELECT emp_id, emp_salary FROM EMPLOYEES
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO @EMPID, @SALARY
WHILE @@FETCH_STATUS = 0
BEGIN
  SELECT @BONUS=(@SALARY * 0.1)
  INSERT INTO EMP_BONUS
  VALUES
  (@EMPID, @BONUS)
 FETCH NEXT FROM MyCursor INTO @EMPID, @SALARY
END
CLOSE MyCursor
DEALLOCATE MyCursor
As we have seen, with the use of a cursor, you can create pretty neat loops in your code.
The cursor itself is NOT the loop. It acts like a virtual table which defines the "range" that the WHILE loop
operates on. So, if you want to pinpoint the actual loop in the code, it's ofcourse the WHILE loop.
Notes:
(1):
There are more "types", or "variations", of cursors, than is shown in this simple document.
You can easily see that from this basic TSQL declare syntax:
DECLARE cursor_name CURSOR[ LOCAL | GLOBAL ][ FORWARD_ONLY | SCROLL ][ STATIC | KEYSET |
DYNAMIC | FAST_FORWARD ][ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ][ TYPE_WARNING ]
FOR select_statement[ FOR UPDATE [ OF column_name [ ,...n ] ] ]
See for example BOOKS ONLINE for more information.
(2):
Using a cursor to plough through really large tables, might not be a good idea.
Everybody "has different ideas" of what is actually "large", but be carefull.
A cursor is great for tables with a number of rows < 500000 (arbitrarily choosen),
but using tables with many millions of rows, might prove to be problematic.
First, such a cursor is always very memory consuming, and if you declare a cursor using a large table
and update that same table in the script, serious locking issues may arise, if users are
are modifying that table as well.
Not withstanding these warnings, a cursor is great for using in loops, if you have relatively smaller
datasets, just as in example 1, for administrative scripts for the DBA.
 
1.10. HOW TO KILL A SESSION (SPID):
This is easily done from a query tool, like the Query Window in SSMS.
If you have found the "spid", that is, the "SQL Server Process ID", (or Session ID as it is nowadays called),
that you need to terminate, then issue the command:
kill @@spid
where the @@spid variable is the session id, like for example 178.
So, in this case, the command would be:
kill 178
1.11. HOW TO FIND AN OBSTRUCTING, OR BLOCKING, SESSION:
Normally, transactions goes so fast that other users will not notice the locking features of SQL Server.
Locks (for example a rowlock, or a pagelock) is just a normal mechanism in ensuring integrety in multi-user access.
But sometimes a user (or the program the user is using), will hold a lock too long, and thus it might
block other users for accessing the resource.
Often "killing" the obstructing session might be the only way to let the other users continue their work.
How to kill a session was shown in section 1.10.
Finding blocking sessions was already shown in section 1.4.
But let's emulate a typical case here.
Example:
First, in your test database, create a test table "X" like:
CREATE TABLE X
(
id INT,
name VARCHAR(20))
Having just one testrecord in X, is enough for our demo, so let's insert this one:
INSERT INTO X VALUES (1,'hammer')
Now let's create a (standard) SQL login, with permissions in your TEST database:
CREATE LOGIN mary WITH PASSWORD='welcome01', DEFAULT_DATABASE=TEST
GO
USE TEST
GO
EXEC sp_grantdbaccess 'mary', 'mary'
GO
EXEC sp_addrolemember 'db_datareader', 'mary'
GO
EXEC sp_addrolemember 'db_datawriter', 'mary'
GO
Since mary is granted the db_datareader and db_datawriter roles, she may modify table X.
Let's start a session as mary. Go to the OS prompt, and start a "sqlcmd" session:
C:\> sqlcmd -U mary -P welcome01
1> use TEST
2> GO
Changed database context to 'TEST'.
1> BEGIN TRANSACTION
2> UPDATE X
3> SET NAME='bycicle' WHERE ID=1
4> GO
(1 rows affected)
Note that mary started a TRANSACTION, but not committed it yet. This means she holds an exclusive rowlock
on that specific record in table X.
Now you start a seperate independent Query Window in SSMS (or also use a sqlcmd session).
Try to select the table X.
SELECT * FROM x
Your query will excecute, but no resultset will show. Your session is blocked by the session of mary.
Let's view the blocking lock: (query is usable in 2000/2005/2008)
SELECT spid, cpu, physical_io, blocked, cmd, waittime,
substring(convert(varchar(20),last_batch), 1, 20) as "LASTBATCH",
substring(nt_username, 1, 15) AS "USERNAME",
substring(loginame, 1, 20) AS "LOGINNAME",
substring(hostname, 1, 15) AS "HOSTNAME",
substring(program_name, 1, 40) AS "PROGRAM"
FROM master.dbo.sysprocesses
This should cleary show that your session is blocked by mary's session.
Also, let's try the following query (query is usable in 2005/2008)
SELECT
s.login_name, s.nt_user_name,
r.session_id AS BLOCKED_SESSION_ID,
r.blocking_session_id AS BLOCKING_SESSION_ID,
s.program_name,
r.start_time,r.status,r.command,database_id,
r.wait_type,r.open_transaction_count,r.percent_complete,r.cpu_time,r.reads,r.writes,r.deadlock_priority
from sys.dm_exec_sessions s, sys.dm_exec_requests r
where s.session_id=r.session_id AND blocking_session_id > 0
That query will show your blocked session id, and the offending session id.
To end the lock, you might consider killing that blocking session id, with:
kill blocking_session_id
Like for example:
kill 52
1.12. SOME OFTEN USED STANDARD FUNCTIONS:
In this section, it's handy to have a small table with a few demo records,
so, let's make the following demo table in a test database:
CREATE TABLE B
(
id INT,
name VARCHAR(100))
Now, let's insert these few test records:
INSERT INTO B VALUES (1,'123:BB:Johnson') -- note that there is a sort of "code" before the name Johnson
INSERT INTO B VALUES (2,'  Carlson') -- note that there is white space before the name Carlson
INSERT INTO B VALUES (3,'I want a lot of gold in my bathroom')
SELECT * FROM B
Resultset:
id  name
1   123:BB:Johnson
2     Carlson
3   I want a lot of gold in my bathroom
For any of the following functions, it's true that they can operate on fields of a table in a SELECT query, and
they can equally well operate on a variable.
For example:
SELECT substring(name, 1, 10) from EMPLOYEE
Here, we used the substring() function to retrieve just a part of the name field of the EMPLOYEE table in a SELECT query.
SELECT @smallvar=substring(@bigvar,10,5)
In the above statement, we assigned the "@smallvar" variable, a value that's just a part of the "@bigvar" variable.
1. The LTRIM() and RTRIM() functions:
Suppose somebody mistakingly inserted the (2,'  Carlson;) row into table B. The mistake then is the white space
on the left of the name 'Carlson'. If you want to Left trim, or Right trim spaces from character fields (or variables), you
can use the LTRIM and RTRIM functions.
Function LTRIM: Removing leading spaces.
Function RTRIM, Removing trailing spaces.
Example 1:
Just compare the resultsets from the two queries below:
SELECT name FROM B
123:BB:Johnson
  Carlson
I want a lot of gold in my bathroom
SELECT LTRIM(name) FROM B
123:BB:Johnson
Carlson
I want a lot of gold in my bathroom
Example 2:
Now let's demonstrate LTRIM and RTRIM using a piece of TSQL code:
DECLARE @varwithspaces varchar(100)
DECLARE @varwithoutspaces varchar(100)
SELECT @varwithspaces=' the_text '
SELECT @varwithoutspaces=LTRIM(RTRIM(@varwithspaces))
-- Now show both variables:
SELECT @varwithspaces
SELECT @varwithoutspaces
If you execute above code, you should see this:
  the_text
(1 row(s) affected)
the_text
(1 row(s) affected)
2. The substring() function:
The substring() function has the following syntax:
SUBSTRING(string, startposition, length)
So you can use it if you want to "clip" a field or variable, that is, you want only a piece of the text,
starting from position "startposition" with a lenght of "length".
Example 1.
Take a look at the following piece of TSQL:
DECLARE @bigvar varchar(100)
DECLARE @smallvar varchar(100)
SELECT @bigvar='12345678910abcdefghi'
SELECT @smallvar=substring(@bigvar,10,5)
-- Now show both variables:
SELECT @bigvar
SELECT @smallvar
If you would execute the above code fragment, you should see this for output:
12345678910abcdefghi
(1 row(s) affected)
10abc
(1 row(s) affected)
Note how the "10abc" piece of text, is exactly the original string as of position 10 with a length of 5 characters.
Example 2.
The example below, should speak for itself.
CREATE TABLE TABLONG
(
mycolumn VARCHAR(128)
)
CREATE TABLE TABSHORT
(
mycolumn VARCHAR(128)
)
INSERT INTO TABLONG VALUES ('00123-CODE:09876')
INSERT INTO TABLONG VALUES ('00123-CODE:09877')
SELECT * FROM TABLONG
mycolumn
------------------------
00123-CODE:09876
00123-CODE:09877
(2 row(s) affected)
INSERT INTO TABSHORT
SELECT SUBSTRING(mycolumn,12,5)
FROM TABLONG
Notice that the rows in table TABSHORT are the clipped data from table TABLONG.
Note: in case you did not know, it's indeed possible to INSERT rows into a table, using a SELECT from another table.
SELECT * FROM TABSHORT
mycolumn
-------------------------------
09876
09877
(2 row(s) affected)
3. The LEN() function:
This one is very easy. It returns the length of a string, or field of a table.
Example 1:
If you had created table "B" as was shown in the beginning of this section, then take a look at this:
SELECT LEN(NAME) FROM B
-----------
14
10
35
(3 row(s) affected)
Note how LEN() just returns how many characters a field or variable has. Note that also spaces are counted.
So, LEN() is very usable to determine the length of a field or variable.
In other sections, we will find very good use of the LEN() function, I assure you!
Example 2:
Take a look at this TSQL:
DECLARE @MyVar varchar(100)
SELECT @MyVar='This is some text'
SELECT @MyVar
SELECT LEN(@MyVar)
If you run that code, you should see:
This is some text
(1 row(s) affected)
17
(1 row(s) affected)
4. The REPLACE() function:
This is a great function, to replace existing text in a field of a table (or variable),
by some other text.
Syntax:
REPLACE(field, 'string_to_be_replaced', 'replacement')
Example 1:
DECLARE @MyString VARCHAR(128)
DECLARE @MyOtherString VARCHAR(128)
SET @MyString='The great Mr. A'
SET @MyOtherString=REPLACE(@MyString,'Mr','Mrs')
SELECT @MyOtherString
------------------
The great Mrs. A
(1 row(s) affected)
Example 2:
Suppose you have this data in the INVENTORY table:
id   Article   Manufacturer
5   bycicle   ABC Corp
6   Hammer   XYZ Corp
7   MotorOil   XYZ Corp
8   Roadmap   EFG Corp
etc..
Now, suppose we need to replace the XYZ in "XYZ Corp" data, with DEVO:
UPDATE INVENTORY
SET manufacturer=REPLACE(manufacturer, 'XYZ','DEVO')
Then the manufacturer field, only where the data contained 'XYZ', will be changed to contain 'DEVO'.
id   Article   Manufacturer
5   bycicle   ABC Corp
6   Hammer   DEVO Corp
7   MotorOil   DEVO Corp
8   Roadmap   EFG Corp
etc..
5. The PATINDEX() function:
In the next section (section 1.13), we will see a great example of the usuability of this function.
Syntax:
PATINDEX("pattern", field or variable)
Essentially, the PATINDEX() function works like this.
If you want to find the starting position of where a string of a certain "pattern" starts from
in a column of a table, or any expression, you can USE the PATINDEX("pattern", column) function.
An example will illustrate this.
Example 1:
In the beginning of this section, we created table "B", and here is it's content:
SELECT * FROM B
Resultset:
id  name
1   123:BB:Johnson
2     Carlson
3   I want a lot of gold in my bathroom
Now suppose I want to know where the "pattern" "gold" starts from in record no 3,
we can use this:
SELECT PATINDEX('%gold%',name)
FROM B WHERE id=3
If you run that statement, you would find that the pattern %gold% starts from position 17.
So, the PATINDEX() function returns an integer.
See the next section for a useable example.
6. The CHARINDEX() function:
This function returns the starting position of the "expression1" in a character string (expression2).
So, it works remarkably the same as the function PATINDEX(). There are a few minor differences though.
PATINDEX can use wildcard characters, but CHARINDEX cannot.
Syntax: CHARINDEX ( expression1 , expression2 [ , start_location ] )
If "expression1" in "expression2" is not found, the function returns "0".
Here are a few simple examples:
SELECT CHARINDEX('/','abcd/efgh')
Will return "5".
SELECT CHARINDEX('John','This morning John went to work, but he really disliked it')
Will return "14".
1.13. REMOVING "UNWANTED" CHARACTERS FROM A FIELD OR STRING:
Suppose you have a variable, containing "unwanted" characters like "\", or "%" etc..
and suppose you need to "clean" that, so that only ordinary characters like a-z, A-Z, and 0-9 remain,
the following script might be handy.
To appreciate what the script does, copy/paste it in a Query Window and execute it.
DECLARE @s varchar(100)
DECLARE @i INT
SELECT @s = 'asd i/.,<>as>[{}]vnbv' -- assign a value to variable @s, so before cleaning
SELECT @s -- show @s as it is now
SELECT @i = patindex('%[^a-z^A-Z^0-9^ ]%', @s)
  while @i > 0
  BEGIN
    SELECT @s = replace(@s, substring(@s, @i, 1), '')
    SELECT @i = patindex('%[^a-z^A-Z^0-9^ ]%', @s)
  END
SELECT @s -- show @s as it is after "cleaning
Then these would be the values of @s before and after the cleaning:
asd i/.,<>as>[{}]vnbv
asd iasvnbv
Ofcourse, we can apply that to fields of a table as well.
Just take a look at this example:
CREATE TABLE Z
(
id INT,
name VARCHAR(20))
Having just one testrecord in Z, is quite enough for our demo, so let's insert this one:
INSERT INTO Z VALUES (1,'abc/vv>()&%aaa')
SELECT * FROM Z
Resultset:
id name
1 abc/vv>()&%aaa
Now lets run the following script:
DECLARE @s varchar(100)
DECLARE @i INT
SELECT @s = (SELECT NAME FROM Z WHERE ID=1)-- assign a value to variable @s
SELECT @s -- show @s as it is now
SELECT @i = patindex('%[^a-z^A-Z^0-9^ ]%', @s)
  while @i > 0
  BEGIN
    SELECT @s = replace(@s, substring(@s, @i, 1), '')
    SELECT @i = patindex('%[^a-z^A-Z^0-9^ ]%', @s)
  END
SELECT @s -- show @s as after "cleaning
The two "SELECT @s" in the above script, will produce:
abc/vv>()&%aaa -- the first select @s
abcvvaaa -- the second select @s
So, we succeeded in removing the unwanted characters. The main mechanism for this to work, is the
PATINDEX() function, which we have seen (among other functions) in the former section.
1.14. REMOVING "UNWANTED" QUOTES FROM A FIELD OR STRING:
Suppose you have a table with rows like:
name city
'Harry' 'Boston'
'Miriam' 'Seattle'
etc..
where all fields are enclosed by ''.
To create a new table without these quotes around the data, you can use the example below.
Maybe you can create a more "elegant" or more "efficient" piece of TSQL code.
Actually, the script below is not really good enough to run on any table of value.
It only demonstrates the use of the LEN() function, as was discussed in the former section.
Example:
Let's create the table (TAB1) first and insert some rows with quotes around the data.
Then we create a table (TAB2) with the same structure as TAB1, which will hold the "cleaned" rows after the script has run.
CREATE TABLE TAB1 -- name the table TAB1 if you want to follow below script
(
name varchar(32),
city varchar(32)
)
CREATE TABLE TAB2 -- name the table TAB2 if you want to follow below script
(
name varchar(32),
city varchar(32)
)
INSERT INTO TAB1 VALUES ('''Harry''', '''Boston''')
INSERT INTO TAB1 VALUES ('''Miriam''', '''Seattle''')
Note the use of '''text''', in order to insert text data surrounded with quotes, into the table.
Let's see what's in table TAB1:
name   city
'Harry'   'Boston'
'Miriam'   'Seattle'
Now, let's fill table "tab2" using the following script.
DECLARE @name varchar(32)
DECLARE @city varchar(32)
DECLARE @length_name INT
DECLARE @length_city INT
DECLARE @name2 varchar(32)
DECLARE @city2 varchar(32)
DECLARE cur1 CURSOR FOR
SELECT name, city FROM tab1
OPEN cur1
FETCH NEXT FROM cur1 INTO @name, @city
WHILE (@@fetch_status<>-1)
BEGIN
SELECT @length_name =LEN(@name)
SELECT @length_city =LEN(@city)
SELECT @name2 =substring(@name,2,@length_name-2)
SELECT @city2 =substring(@city,2,@length_city-2)
INSERT INTO TAB2
values
(@name2,@city2)
FETCH NEXT FROM cur1 INTO @name, @city
END
CLOSE cur1
DEALLOCATE cur1
Note:
Since we are busy with quotes anyway, suppose you need to assign a value to a (string) variable,
where the value includes a quote, you can do it like so:
DECLARE @VAR1 VARCHAR(64)
SET @VAR1='Appie''s'
SELECT @var1 -- this statement shows the value of @VAR1
You should see the result:
Appie's
1.15. SIMPLE OVERVIEW MOST IMPORTANT SQL SERVER DATATYPES:
SQL Server uses "strong typing". In this case it means that suppose a column of a table is of datatype "datetime",
then you cannot insert just any number into that field. Likewise, if a column is of datatype "integer", you cannot
just put a number with decimals into that field.
Sometimes you must be able to "convert" a variable or field of a certain datatype, into another datatype.
SQL Server allows you to use a large number of different datatypes.
We all know the "administrative like" datatypes like integer, decimal(n,m), datetime, varchar(n) and char(n),
which are often used in many administrative databases (administration, bookkeeping, orderentry, logistics etc..).
But there are lots more, like the XML datatype, which is native to store XML, or varbinary which you can use
to store any binary (like docs etc..)
Here are the most often used datatypes, with a short description:
________________________________________________________________________________________________
character based data:
________________________________________________________________________________________________
char(length): A fixed length character datatype. For example, "char(4)" will always reserve 4 bytes of space.
________________________________________________________________________________________________
nchar(length): just like char, but this time UNICODE
________________________________________________________________________________________________
varchar(length): A variable length character datatype. It will only store what's needed, up to the specified length.
________________________________________________________________________________________________
nvarchar(length): just like varchar, but this time UNICODE
________________________________________________________________________________________________
text: if you need to store a lot of text. Max 1G characters.
________________________________________________________________________________________________
ntext: just like text, but now in UNICODE
________________________________________________________________________________________________
nummeric based data:
________________________________________________________________________________________________
decimal(n,m): decimal notation/type, meaning n digits before the "," or "." seperator, and m precision after.
________________________________________________________________________________________________
nummeric(n,m): same as decimal(n.m)
________________________________________________________________________________________________
float(n): can be used for large floating point numbers
________________________________________________________________________________________________
bigint: whole numbers=integers: -2^63 to 2^63; use 8 bytes storage
________________________________________________________________________________________________
int: whole number=integers: -2^31 to 2^31; uses 4 bytes storage
________________________________________________________________________________________________
smallint: whole number=integers: -32768 to 32767; uses 2 bytes storage
________________________________________________________________________________________________
tinyint: whole numbers=integers 0 to 255; uses 1 byte storage
________________________________________________________________________________________________
date/time datatype:
________________________________________________________________________________________________
datetime: stores date + time (accuracy 0.00333 s); 8 bytes; 01/01/1753 - 31/12/9999
________________________________________________________________________________________________
datetime2: stores date + time (accuracy 100 ns); 6 to 8 bytes; 01/01/0001 - 31/12/9999
________________________________________________________________________________________________
smalldatetime: like above, but a "smaller" variant; 4 bytes; 01/01/1900 - 06/06/2079
________________________________________________________________________________________________
date: stores just plain dates like "12/12/2010"; 3 bytes
________________________________________________________________________________________________
time: stores plain time, with accuracy of 100 ns; 3 to 5 bytes
________________________________________________________________________________________________
binary datatypes:
________________________________________________________________________________________________
image: e.g.: to store pdf, word files, images, or other binary data; up to 2G
________________________________________________________________________________________________
varbinary(max): e.g.: to store pdf, word files, images, or other binary data; up to 2G
________________________________________________________________________________________________
binary: Fixed-length binary data with a length of n bytes, where n from 1 through 8,000. storage is n bytes.
________________________________________________________________________________________________
XML datatype:
________________________________________________________________________________________________
XML: "native" datatype to store XML documents or fragments
________________________________________________________________________________________________
Spatial Datatype:
________________________________________________________________________________________________
geometry: supports geometric data (points, lines, and polygons)
________________________________________________________________________________________________
geography: a Euclidean coordinate system. This type represents geographic objects on an area on the Earth's surface
________________________________________________________________________________________________
uniqueidentifier:
________________________________________________________________________________________________
uniqueidentifier: Globally Unique ID, which is "supposed" to uniquely determine a row, between entities, instances, and objects.
________________________________________________________________________________________________
Choose your datatype carefully:
If you choose "too wide" datatypes, you fill a database block "too quickly". This means that a smaller number of rows
fits in a table page.
This cost performance, because the database needs to read more pages, to get the same information.
For example, suppose some table has a "COMMENT" column. Think of the consequence if some developer choose a
a datatype of "char(2000)", meaning a fixed column lenght of 2000 bytes.
Now if the comment is at most just a few words, then that's a true waste.
As another example: If you are sure you can use a smallint, then don't take a bigint.
Carefully choosing datatypes, not only saves space, but will also lower computing power as well.
1.16. THE CAST AND CONVERT CONVERSION FUNCTIONS:
Sometimes you must be able to "convert" a variable, or field, of a certain datatype, into another datatype.
Ofcourse, some datatypes are very similar (like char, varchar). For those, SQL Server will use "implicit conversion"
at certain events.
For example, if you have a table with char columns, and you insert those rows to another (similar) table with varchar columns,
then that will work (provided that the varchar columns are not too small to hold the char values).
But at other times, you really need to "convert" values from one type to another.
For this, the CAST() and CONVERT() conversion functions can be used. CAST() and CONVERT() are almost equal in functionality.
You can say that CONVERT() offers somewhat more options to pick from.
We are going to illustrate the use of CONVERT and CAST with a couple of examples.
The syntax for CAST() is:
CAST ( expression AS data_type [ (length ) ])
The syntax for CONVERT is:
CONVERT ( data_type [ ( length ) ] , expression [ , style ] )
The syntax might look "difficult", which it is not.
The "expression" is a variable, or field" that you want to convert.
So let's take a look at a few examples:
Example 1
In this example, we will use one of SQL Server "internal" functions. The function GETDATE(), just gets the current date/time,
and we can use it to assign a value to a variable of datatype "datetime".
DECLARE @orderdate DATETIME
DECLARE @orderdate_as_char VARCHAR(30)
SELECT @orderdate=GETDATE() -- now @orderdate will be assigned a value
SELECT @orderdate_as_char=CAST(@orderdate AS varchar(30))
-- Let's print the values of the variables:
SELECT @orderdate
SELECT @orderdate_as_char
If you run the above code, you will see something like the following output.
Your specific output may differ somewhat, depending on the "collation" of your system.
2011-05-07 16:28:37.630
(1 row(s) affected)
May 7 2011 4:28PM
(1 row(s) affected)
Exercise: run the same code, but this time use "CAST(@orderdate AS varchar(10))", so now we convert
to a varchar of length 10. What do you see?
Notice that the syntax of CAST (as shown above), actually is no more than:
CAST ( variable_or_field AS new_data_type [ (length ) ])
Example 2
DECLARE @l_rowcount INT
SET @l_rowcount=30 -- Also SET can be used to assign a value to a variable
PRINT 'NUMBER OF ROWS PROCESSED: '+@l_rowcount
If we run that code, we get:
Msg 245, Level 16, State 1, Line 4
Conversion failed when converting the varchar value 'NUMBER OF ROWS PROCESSED: ' to data type int.
So, the PRINT statement, to print to your screen, expects concatenations (using "+") to be of the same type.
That is,
PRINT 'some string'+@string_variable
is OK.
So let's change the third line in above code into:
PRINT 'NUMBER OF ROWS PROCESSED: '+CAST(@l_rowcount AS varchar)
and now all is well!
Example 3
The CONVERT() function works exactly the same as CAST(). Only, if you take a look at both syntaxes as shown above,
you can use additionally (and optionally) in CONVERT(), a "style" clause, which determines the formatting.
Let's take a look at an example.
DECLARE @orderdate DATETIME
DECLARE @orderdate_as_char VARCHAR(30)
SELECT @orderdate=GETDATE() -- now @orderdate will be assigned a value, which is the current date/time
-- Now let's try different "styles" and see what we get
SELECT CONVERT(varchar(30), @orderdate) -- no style selected
SELECT CONVERT(varchar(30), @orderdate, 113)
SELECT CONVERT(varchar(30), @orderdate, 111)
SELECT CONVERT(varchar(30), @orderdate, 102)
SELECT CONVERT(varchar(30), @orderdate, 20)
SELECT CONVERT(varchar(30), @orderdate, 10)
SELECT CONVERT(varchar(10), @orderdate, 20)
Try to run the above code. You should see various representations.
1.17. A FEW WORDS ON GRANTING ROLES AND PERMISSIONS TO LOGINS:
This is not a section where you get a formal presentation on how to deal with security and permissions in SQL Server.
SQL Server permissions can be granted on server, database, schema, and database object level.
Instead, here only a quick practical intro is presented on how to work with roles
and how you can grant permissions to users and roles. Also we will show how to script the memberlist of roles.
There are occasions where working with the graphical SSMS is really easy, like granting a role to a login.
But suppose you want a lot of logins to grant a certain role or permission (e.g.: to a table).
Then scripting is the way to go (this is not to say that the graphical studio does not provide you lots of tools as well).
There are two "main" ways here:
- You can grant a "role" to a login. The role is comparable to a group, with a predefined set of permissions. Using TSQL, you
will use the specialized system stored procedures to add a user to a role.
- You can grant a certain "permission" to a login (like select, or execute), on a certain object (like a table, stored procedure).
Using TSQL, you will use the GRANT statement.
In SQL Server documentation, the "talk" is like this:
You GRANT a role or permission TO a "principal". It's an expensive name for anything
that can access the Server. So, the common principal we can think of, is a login.
You GRANT a role or permisson ON a "securable", which is an object
like a Table or View.
Only one thing is very important here:
- You grant a permission (like SELECT), TO a login, ON an object (like a Table), using the GRANT statement.
So, if you are already at "home" with for eaxmple Oracle, GRANTING a permission works exactly the same
in SQL Server, like for example:
GRANT SELECT ON hr.EMPLOYEES TO harry
- Granting a user a role (or adding a user to a role) has to be done using a system stored procedure.
But, in most databases, like Oracle, you use the GRANT statement as well when you grant a role.
In SQL Server it is a bit different: here you need to use a few specific system stored procedures,
like for example:
EXEC sp_addrolemember 'db_datareader', 'mary'
Let's take a look at roles first.
.
1.17.1 Roles:
There are actually three types of roles:
- database roles: every database has a number of roles with predefined permissions.
- server roles: every server/instance has one set of server roles with predefined permissions.
- application roles: enables an application to run with its own, user-like privileges.
- Database roles:
Every database under your Instance, has the same set of database roles.
Each role has a certain predefined set of permissions.
For example, the "db_datareader" role has the permission to SELECT any table of the database.
You probably agree that's not too hard to derive from the "name" of the role, what it's purpose is.
The following database roles are present in any database:
________________________________________________________________________________________________
db_datareader: members can select any user table
________________________________________________________________________________________________
db_datawriter: members can select, insert, update, delete statements
________________________________________________________________________________________________
db_accessadmin: members can control access to the database
________________________________________________________________________________________________
db_backupoperator: members can backup the database
________________________________________________________________________________________________
db_ddladmin: members can issue ddl (create and modify objects) c
________________________________________________________________________________________________
db_denydatareader: members cannot read any data in the user tables within a database
________________________________________________________________________________________________
db_denydatawriter: members cannot modify any data in the user tables within a database
________________________________________________________________________________________________
db_owner: members have all permissions
________________________________________________________________________________________________
db_securityadmin: members control permissions on securables
________________________________________________________________________________________________
public: role where all users are member of. Should have the least possible permissions
________________________________________________________________________________________________
- To assign a user a database role (grant a role) use the "sp_addrolemember" stored procedure, like for example:
USE Your_database_name
GO
EXEC sp_addrolemember 'db_datareader', 'mary'
GO
- To remove a user from a database role, use:
USE Your_database_name
GO
EXEC sp_droprolemember 'db_datareader', 'mary'
It is possible to create a database role, if you feel that the existing roles do not conform to your
security policies. You can create a database role, then grant the neccessary permissions to it, and
grant the role to users.
You can create a database role like so:
USE Your_database_name
GO
CREATE ROLE role_name
GO
- Server roles:
These are "server wide" roles. So there is only one set of "server roles" per Instance.
Regular database users should not be member of these roles. They are usually meant for users
with dba and admin duties.
The following Server roles are present for your Server:
________________________________________________________________________________________________
bulkadmin: Granted: ADMINISTER BULK OPERATIONS
________________________________________________________________________________________________
dbcreator: Granted: CREATE DATABASE
________________________________________________________________________________________________
diskadmin: Granted: ALTER RESOURCES
________________________________________________________________________________________________
processadmin: Granted: ALTER ANY CONNECTION, ALTER SERVER STATE
________________________________________________________________________________________________
securityadmin: Granted: ALTER ANY LOGIN
________________________________________________________________________________________________
serveradmin: Granted: ALTER ANY ENDPOINT, ALTER RESOURCES, ALTER SERVER STATE, ALTER SETTINGS, SHUTDOWN, VIEW SERVER STATE
________________________________________________________________________________________________
setupadmin: Granted: ALTER ANY LINKED SERVER
________________________________________________________________________________________________
sysadmin: (has full power) Granted with GRANT option: CONTROL SERVER
________________________________________________________________________________________________
- To assign a user Server role, use the "sp_addsrvrolemember " stored procedure, like for example:
EXEC sp_addsrvrolemember 'mary', 'sysadmin'
GO
- To remove a user from a server role, use the "sp_dropsrvrolemember " stored procedure, like for example:
EXEC sp_dropsrvrolemember 'mary', 'sysadmin'
1.17.2 Granting Permissions:
The database roles 'db_datareader" and "db_datawriter", might have a too wide range of permissions.
What is possible, is that you GRANT permissions to a user on a selection of tables and other objects.
What is even better, is that you create a role, grant the neccessary permissions to that role, and as
the last step, grant the role to users.
This is better, since you then do not grant permissions to (possibly a large) number of users.
You only need to add and remove users to that role, as neccessary.
Anyway, you need to know how to grant individual permissions on objects (securables) to principals (users, roles),
using the GRANT statement.
There are very many classes on which the GRANT statement can operate.
The most import ones are:
DML Data Manipulation, or "Object Level Rights", like granting select, insert, update, delete, execute permissions.
DDL Data Definition: like granting CREATE object, DROP object permissions.
Here are a few representative examples:
GRANT SELECT on CONTACTS to harry
GRANT SELECT, INSERT on EMPLOYEE to harry
GRANT SELECT, INSERT, UPDATE on ORDERS to SALESUSERS -- a database role you created
GRANT EXECUTE on stp_InsertEmployee to SALESUSERS
For stored procedures, the additional remarks are important:
USE SALES;
GRANT EXECUTE ON OBJECT::HumanResources.uspUpdateEmployeeHireInfo
TO username;
GO
GRANT EXECUTE ON dbo.procname TO username;
GRANT VIEW SERVER STATE TO username;
IMPORTANT:
grant view server state to [Domain\opsmgr-sql]
go
grant view any definition to [Domain\opsmgr-sql]
go
GRANT VIEW DEFINITION ON [dbo].[usp_GetUserDetails] TO [username]
EXECUTE AS user = 'special_user'
EXECUTE YourProcerdure
REVERT
-- Use the following if the sp creates objects....
CREATE PROCEDURE dbo.usp_Demo2
WITH EXECUTE AS OWNER
AS
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].table_2') AND type in (N'U'))
CREATE TABLE table_2 (id int, data nchar(10))
INSERT INTO table_2
SELECT top 5 * from dbo.table_1;
GO
GRANT EXEC ON dbo.usp_Demo1 TO test;
GO
works!!!
END IMPORTANT:
DENY statement is possible too, like in:
DENY INSERT,UPDATE,DELETE,TAKE OWNERSHIP ON EMPLOYEE To harry
DENY INSERT,UPDATE,DELETE,TAKE OWNERSHIP ON ORDERS To harry
1.18. A FEW WORDS ON SHOWING PERMISSIONS USING TSQL:
1.18.1 Getting a list of Server role members:
Most critical for viewing permissions, is to see which accounts are member of which Server roles.
For example, the "sysadmin" Server role has no limitations whatsoever, so you must be extra careful
on who is member here.
SELECT
rolename = rolep.name,
membername = memp.name
FROM
sys.server_role_members rm
JOIN sys.server_principals rolep ON rm.role_principal_id = rolep.principal_id
JOIN sys.server_principals memp ON rm.member_principal_id = memp.principal_id
1.18.2 Getting a list of Domain Group members:
If you want to see which members exists in a Domain Group, which is added as a "login" in SQL Server,
then the "xp_logininfo" stored procedure can be used. It can use a number of parameters, which you can easily lookup.
This can be handy, if you do not have AD access, while you still want to see which members exist in a certain AD group
which was added in SQL Server.
For example:
exec xp_logininfo 'MyDomain\MyGroup', 'members'
1.18.3 Getting a list of Database role members:
For a specific database:
Per database, you can use the "sp_helpuser" stored procedure, to see which database users,
are member of which database role(s).
e.g.:
USE database_name
GO
exec sp_helpuser
You can also use:
USE database_name
GO
select user_name(role_principal_id) AS ROLE, user_name(member_principal_id) AS USERNAME
from sys.database_role_members
For all databases in one run:
The following script generates statements, which you can run to view all database role members
over all databases.
DECLARE @dbname VARCHAR(64)
DECLARE cur1 CURSOR FOR
SELECT name FROM sys.databases
where name not in ('master', 'msdb','model','tempdb')
OPEN cur1
FETCH NEXT FROM cur1 INTO @dbname
WHILE (@@fetch_status<>-1)
BEGIN
PRINT '--FOR DATABASE: '+@dbname
PRINT 'USE ['+@dbname+']'
PRINT 'GO'
PRINT 'PRINT '+''''+@dbname+''''
PRINT 'GO'
PRINT 'exec (''sp_helpuser'')'
PRINT 'GO'
PRINT ' '
FETCH NEXT FROM cur1 INTO @dbname
END
CLOSE cur1
DEALLOCATE cur1
1.18.4 Getting other listings:
Let's see if we can build scripts that will create listings.
Before we do so, I like to attend you on a specific system view, namelely "sys.all_objects".
This view indeed has information on almost all objects, so on other "system views" as well !
The columns in this view, which I think are of interest, are "name" and "type_desc".
So, if you want to know which dictionary system view has information on "principals", or "table" etc..,
you might try queries like:
select name, type_desc from sys.all_objects where name like '%princip%'
select name, type_desc from sys.all_objects where name like '%role%'
select name, type_desc from sys.all_objects where name like '%permis%'
select name, type_desc from sys.all_objects where name like '%protect%'
select name, type_desc from sys.all_objects where name like '%user%'
etc..
script 1: Viewing permissions on Tables in a database. Usable for 2005/2008/2012
USE database_name
GO
select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES
script 2: Viewing permissions. usable for 2005/2008/2012
USE database_name
GO
select sys.schemas.name 'Schema'
, sys.objects.name Object
, sys.database_principals.name username
, sys.database_permissions.type permissions_type
, sys.database_permissions.permission_name
, sys.database_permissions.state permission_state
, sys.database_permissions.state_desc
, state_desc + ' ' + permission_name + ' on ['+ sys.schemas.name + '].[' + sys.objects.name + '] to [' + sys.database_principals.name + ']' COLLATE LATIN1_General_CI_AS
from sys.database_permissions
join sys.objects on sys.database_permissions.major_id = sys.objects.object_id join sys.schemas on sys.objects.schema_id = sys.schemas.schema_id join sys.database_principals on sys.database_permissions.grantee_principal_id = sys.database_principals.principal_id order by 1, 2, 3, 5
script 3: Viewing permissions. usable for 2005/2008/2012 (sometimes this fails)
SELECT CASE WHEN P.state_desc = 'GRANT_WITH_GRANT_OPTION' THEN 'GRANT' ELSE P.state_desc END AS cmd_state,
P.permission_name,
'ON '+ CASE P.class_desc
WHEN 'DATABASE' THEN 'DATABASE::'+QUOTENAME(DB_NAME())
WHEN 'SCHEMA' THEN 'SCHEMA::'+QUOTENAME(S.name)
WHEN 'OBJECT_OR_COLUMN' THEN 'OBJECT::'+QUOTENAME(OS.name)+'.'+QUOTENAME(O.name)+
CASE WHEN P.minor_id <> 0 THEN '('+QUOTENAME(C.name)+')' ELSE '' END
WHEN 'DATABASE_PRINCIPAL' THEN
CASE PR.type_desc
WHEN 'SQL_USER' THEN 'USER'
WHEN 'DATABASE_ROLE' THEN 'ROLE'
WHEN 'APPLICATION_ROLE' THEN 'APPLICATION ROLE'
END +'::'+QUOTENAME(PR.name)
WHEN 'ASSEMBLY' THEN 'ASSEMBLY::'+QUOTENAME(A.name)
WHEN 'TYPE' THEN 'TYPE::'+QUOTENAME(TS.name)+'.'+QUOTENAME(T.name)
WHEN 'XML_SCHEMA_COLLECTION' THEN 'XML SCHEMA COLLECTION::'+QUOTENAME(XSS.name)+'.'+QUOTENAME(XSC.name)
WHEN 'SERVICE_CONTRACT' THEN 'CONTRACT::'+QUOTENAME(SC.name)
WHEN 'MESSAGE_TYPE' THEN 'MESSAGE TYPE::'+QUOTENAME(SMT.name)
WHEN 'REMOTE_SERVICE_BINDING' THEN 'REMOTE SERVICE BINDING::'+QUOTENAME(RSB.name)
WHEN 'ROUTE' THEN 'ROUTE::'+QUOTENAME(R.name)
WHEN 'SERVICE' THEN 'SERVICE::'+QUOTENAME(SBS.name)
WHEN 'FULLTEXT_CATALOG' THEN 'FULLTEXT CATALOG::'+QUOTENAME(FC.name)
WHEN 'FULLTEXT_STOPLIST' THEN 'FULLTEXT STOPLIST::'+QUOTENAME(FS.name)
WHEN 'SEARCH_PROPERTY_LIST' THEN 'SEARCH PROPERTY LIST::'+QUOTENAME(RSPL.name)
WHEN 'SYMMETRIC_KEYS' THEN 'SYMMETRIC KEY::'+QUOTENAME(SK.name)
WHEN 'CERTIFICATE' THEN 'CERTIFICATE::'+QUOTENAME(CER.name)
WHEN 'ASYMMETRIC_KEY' THEN 'ASYMMETRIC KEY::'+QUOTENAME(AK.name)
END COLLATE Latin1_General_100_BIN AS securable,
'TO '+QUOTENAME(DP.name) AS grantee,
CASE WHEN P.state_desc = 'GRANT_WITH_GRANT_OPTION' THEN 'WITH GRANT OPTION' ELSE '' END AS grant_option,
'AS '+QUOTENAME(G.name) AS grantor
FROM sys.database_permissions AS P
LEFT JOIN sys.schemas AS S
ON P.major_id = S.schema_id
LEFT JOIN sys.all_objects AS O
JOIN sys.schemas AS OS
ON O.schema_id = OS.schema_id
ON P.major_id = O.object_id
LEFT JOIN sys.types AS T
JOIN sys.schemas AS TS
ON T.schema_id = TS.schema_id
ON P.major_id = T.user_type_id
LEFT JOIN sys.xml_schema_collections AS XSC
JOIN sys.schemas AS XSS
ON XSC.schema_id = XSS.schema_id
ON P.major_id = XSC.xml_collection_id
LEFT JOIN sys.columns AS C
ON O.object_id = C.object_id
AND P.minor_id = C.column_id
LEFT JOIN sys.database_principals AS PR
ON P.major_id = PR.principal_id
LEFT JOIN sys.assemblies AS A
ON P.major_id = A.assembly_id
LEFT JOIN sys.service_contracts AS SC
ON P.major_id = SC.service_contract_id
LEFT JOIN sys.service_message_types AS SMT
ON P.major_id = SMT.message_type_id
LEFT JOIN sys.remote_service_bindings AS RSB
ON P.major_id = RSB.remote_service_binding_id
LEFT JOIN sys.services AS SBS
ON P.major_id = SBS.service_id
LEFT JOIN sys.routes AS R
ON P.major_id = R.route_id
LEFT JOIN sys.fulltext_catalogs AS FC
ON P.major_id = FC.fulltext_catalog_id
LEFT JOIN sys.fulltext_stoplists AS FS
ON P.major_id = FS.stoplist_id
LEFT JOIN sys.registered_search_property_lists AS RSPL
ON P.major_id = RSPL.property_list_id
LEFT JOIN sys.asymmetric_keys AS AK
ON P.major_id = AK.asymmetric_key_id
LEFT JOIN sys.certificates AS CER
ON P.major_id = CER.certificate_id
LEFT JOIN sys.symmetric_keys AS SK
ON P.major_id = SK.symmetric_key_id
JOIN sys.database_principals AS DP
ON P.grantee_principal_id = DP.principal_id
JOIN sys.database_principals AS G
ON P.grantor_principal_id = G.principal_id
WHERE P.grantee_principal_id IN (USER_ID('TestUser1')); -- or add more users in "IN" clause.
Please change "TestUser1' in the statement above, in the actual database user. You can add more users as well.
The above statement will generate "GRANT" statements for the particular user.
script 4: usable for 2000/2005/2008/2012
SELECT
[UserName] = CASE princ.[type]
WHEN 'S' THEN princ.[name]
WHEN 'U' THEN ulogin.[name] COLLATE Latin1_General_CI_AI
END,
[UserType] = CASE princ.[type]
WHEN 'S' THEN 'SQL User'
WHEN 'U' THEN 'Windows User'
END,
[DatabaseUserName] = princ.[name],
[Role] = null,
[PermissionType] = perm.[permission_name],
[PermissionState] = perm.[state_desc],
[ObjectType] = obj.type_desc,--perm.[class_desc],
[ObjectName] = OBJECT_NAME(perm.major_id),
[ColumnName] = col.[name]
FROM
--database user
sys.database_principals princ
LEFT JOIN
--Login accounts
sys.login_token ulogin on princ.[sid] = ulogin.[sid]
LEFT JOIN
--Permissions
sys.database_permissions perm ON perm.[grantee_principal_id] = princ.[principal_id]
LEFT JOIN
--Table columns
sys.columns col ON col.[object_id] = perm.major_id
AND col.[column_id] = perm.[minor_id]
LEFT JOIN
sys.objects obj ON perm.[major_id] = obj.[object_id]
WHERE
princ.[type] in ('S','U')
UNION
--List all access provisioned to a sql user or windows user/group through a database or application role
SELECT
[UserName] = CASE memberprinc.[type]
WHEN 'S' THEN memberprinc.[name]
WHEN 'U' THEN ulogin.[name] COLLATE Latin1_General_CI_AI
END,
[UserType] = CASE memberprinc.[type]
WHEN 'S' THEN 'SQL User'
WHEN 'U' THEN 'Windows User'
END,
[DatabaseUserName] = memberprinc.[name],
[Role] = roleprinc.[name],
[PermissionType] = perm.[permission_name],
[PermissionState] = perm.[state_desc],
[ObjectType] = obj.type_desc,--perm.[class_desc],
[ObjectName] = OBJECT_NAME(perm.major_id),
[ColumnName] = col.[name]
FROM
--Role/member associations
sys.database_role_members members
JOIN
--Roles
sys.database_principals roleprinc ON roleprinc.[principal_id] = members.[role_principal_id]
JOIN
--Role members (database users)
sys.database_principals memberprinc ON memberprinc.[principal_id] = members.[member_principal_id]
LEFT JOIN
--Login accounts
sys.login_token ulogin on memberprinc.[sid] = ulogin.[sid]
LEFT JOIN
--Permissions
sys.database_permissions perm ON perm.[grantee_principal_id] = roleprinc.[principal_id]
LEFT JOIN
--Table columns
sys.columns col on col.[object_id] = perm.major_id
AND col.[column_id] = perm.[minor_id]
LEFT JOIN
sys.objects obj ON perm.[major_id] = obj.[object_id]
UNION
--List all access provisioned to the public role, which everyone gets by default
SELECT
[UserName] = '{All Users}',
[UserType] = '{All Users}',
[DatabaseUserName] = '{All Users}',
[Role] = roleprinc.[name],
[PermissionType] = perm.[permission_name],
[PermissionState] = perm.[state_desc],
[ObjectType] = obj.type_desc,--perm.[class_desc],
[ObjectName] = OBJECT_NAME(perm.major_id),
[ColumnName] = col.[name]
FROM
--Roles
sys.database_principals roleprinc
LEFT JOIN
--Role permissions
sys.database_permissions perm ON perm.[grantee_principal_id] = roleprinc.[principal_id]
LEFT JOIN
--Table columns
sys.columns col on col.[object_id] = perm.major_id
AND col.[column_id] = perm.[minor_id]
JOIN
--All objects
sys.objects obj ON obj.[object_id] = perm.[major_id]
WHERE
--Only roles
roleprinc.[type] = 'R' AND
--Only public role
roleprinc.[name] = 'public' AND
--Only objects of ours, not the MS objects
obj.is_ms_shipped = 0
ORDER BY
princ.[Name],
OBJECT_NAME(perm.major_id),
col.[name],
perm.[permission_name],
perm.[state_desc],
obj.type_desc--perm.[class_desc]
1.18.5 Some special functions:
The fn_my_permissions() function
The below function applies to SQL Server 2005/2008/2012.
-- Server Level:
select * from fn_my_permissions(NULL, NULL)
-- Database Level:
select * from fn_my_permissions(NULL, 'database')
-- Object level, like the object dbo.EMPLOYEE:
select * from fn_my_permissions('dbo.EMPLOYEE', 'object')
Not only a list of possible permissions can be shown, but you can let the function apply to a user as well:
execute ('select * from fn_my_permissions(NULL, ''database'')') AS USER = 'harry'
The above statement should show you what Database Level permissions harry has.
execute ('select * from fn_my_permissions(''dbo.EMPLOYEE'', ''object'')') AS USER = 'harry'
The above statement should show you permissions harry has on the table EMPLOYEE.
1.19. GET A LIST OF ALL COLUMNNAMES WITH ALL TABLENAMES, AND DATATYPES, USING TSQL:
Sometimes, it just is handy to have an ordered list of all Columnnames, with their datatypes,
and corresponding Tablenames, of all tables in your database.
Here are a few queries that will produce such a list.
-- Script 1: for all SQL Server versions
SELECT substring(c.name, 1, 30) as "ColumName",
c.xtype,
substring(object_name(c.id),1,30) as "TableName",
substring(t.name,1,30) as "DataType"
FROM syscolumns c, systypes t
WHERE c.xtype=t.xtype
ORDER By object_name(c.id)
Optionally you can use a AND (after the WHERE clause), like for example "AND object_name(c.id)='Orders'
-- Script 2: for SQL Server 2005/2008
SELECT * FROM INFORMATION_SCHEMA.COLUMNS -- get all info
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, IS_NULLABLE FROM INFORMATION_SCHEMA.COLUMNS -- get selected info
1.20. A FEW NOTES ABOUT THE "@@" FUNCTIONS:
We already have seen a couple of functions is this note, like for example the string function "REPLACE()", or
the system functions (that are typically used by the DBA) like for example "fn_virtualfilestats()" etc..
With a function, you would typically expect parentheses "(" and ")", where in between you can place the inputvalue(s),
and or expressions, like "myfunction(5)" or "substring(cust_code,1,8)".
In SQL Server, there exists a rather special class of system functions.
Usually, they don;t take values from us, although for some functions a SET statement is allowed.
They have names like "@@functionname", so their names always start with "@@".
They behave in a sense like a Global variable, and you can retrieve it's value by selecting the functionname.
In a moment, you will see some examples.
The "context" in which they apply, is very divers: some functions have only meaning on the Instance Level (like @@cpu_busy),
while others can perfectly be used in your scripts (like @@error).
We already have seen a few of those functions, like @@SPID, which is a "Session ID number" associated with every session.
Example 1:Some system statistical @@ functions
Try the following statements in a Query Window:
select @@CPU_BUSY
select @@IO_BUSY
select @@IDLE
select @@Total_Errors
These are just a few examples of those system statistical @@ functions.
For example, @@CPU_BUSY returns the time that SQL Server has spent working since it was last started.
As another example, @@Total_Errors represent the number of disk write errors encountered by SQL Server since last started.
Ofcourse, it might not be directly trivial how you could benefit from those type of functions,
but maybe it's nice to know they exist.
Example 2: "@@version"
This function returns a resultset, telling you the current version of SQL Server.
So, if we just do this:
select @@version
we find the current version of this SQL Server installation.
Example 3: "@@rowcount" and "@@error"
These are quite usable in your scripts. In section 1.13, we had created a script that removed "unwanted" characters
from a variable, or a field. Now suppose we want to clean a certain field in all rows of a table.
In other words, we want to walk through all rows of that table. Now you might think of using a cursor, or some
other form of a WHILE loop, using some sort of a counter that you need to construct.
Now take a look at the following example, with an quite interesting loop:
-- let's first create a test table:
create table #a
(
s varchar(100)
)
-- now insert a couple of testrecords, with good text and noise text:
INSERT #a (s) SELECT 'asd i/.,<>as>[{}]vnbv'
INSERT #a (s) SELECT 'aaa'
INSERT #a (s) SELECT '123 ''h 9)'
-- what's in the table now?
SELECT * FROM #a
asd i/.,<>as>[{}]vnbv
aaa
123 'h 9)
-- now let's clean the table:
while @@rowcount > 0
update #a
set s = replace(s, substring(s, patindex('%[^a-z^A-Z^0-9^ ]%', s), 1), '')
WHERE patindex('%[^a-z^A-Z^0-9^ ]%', s) <> 0
-- So, what's in the table now?
SELECT * FROM #a
asd iasvnbv
aaa
123 h 9
So, our script worked. But the main thing is: did you noticed how we used the @@rowcount function,
as the "while true" criteria for the while loop? So, sometimes you can really simplify your WHILE loop,
because @@rowcount returns the number of rows of the last statement, or the number of rows associated
with the current TSQL block.
But you can use it at ordinary SQL statements as well, if you want to know how many rows are processed.
For example, if you want to know how many rows were affected at an UPDATE statement, you can use @@rowcount as
in this example:
UPDATE EMPLOYEES
SET DEPARTMENT = 'Amsterdam' WHERE city = 'The Haque'
SELECT @@rowcount AS 'RowsChanged'
1.21. HOW TO SCRIPT YOUR DATABASE:
It's easy to script a Database, including all objects (or a selection), using the graphical Management Studio.
To do that, rightclick your database, choose "Tasks", and next choose "Generate Scripts".
But how to do it from the commandline, so that you can automate the process?
For this, you can use the "sqlpubwiz" utility.
To download it, you might use the following link:
download sqlpubwiz utility from Microsoft (english)
The utility has quite a few prerequisites, but that's all described in the link above.
Using it from the commandline, to script the SALES database, you would use a command
similar to the example below:
C:\> sqlpubwiz script -d SALES -S MYSERVER -U sa -P password C:\sources\sales.sql
1.22. HOW TO GENERATE THE CORRESPONDING INSERT STAMENTS FOR A TABLE WITH DATA:
Maybe you have a certain table with N rows, and you want to generate the corresponding N INSERT statements,
just as if you would have an empty table, and then could use those INSERT statements, to fill it up.
Method 1:
Actually, the "sqlpubwiz" utility from section 1.21, can be used to achieve this.
You can script a complete database, either with structure and all data, or just only the structure of the objects.
So, for just one, or all tables, this tool might be of help.
To get further instruction, use the "help" function of the utility, like in
C:\> sqlpubwiz help script
By the way, if you do not need to use sqlpubwiz in scripts (in automated solutions), you might as well
enter the graphical mode, by just entering "sqlpubwiz" from the prompt, in the directory where the utility was installed.
Method 2:
You can create a stored procedure which will do the job: generate the insert statements for a given table.
You can find many free examples on the internet.
The code below, can be used for tables having administrative datatypes like char(), varchar(), decimal() etc..
When a table uses more "complicated" datatypes, the result may not be what you like.
Create the procedure listed below, in your Database. Suppose you now want to create the INSERT statements for
the CUSTOMERS table, use the following query:
exec generate_inserts CUSTOMERS
Source of the procedure:
create proc generate_inserts @table varchar(20)
--Generate inserts for table @table
AS
declare @cols varchar(1000)
declare @col varchar(50)
set @cols=''
declare colcur
cursor for
select column_name
from information_schema.columns
where table_name=@table
open colcur
fetch next from colcur into @col
while @@fetch_status=0
begin
select @cols = @cols + ', ' + @col
fetch next from colcur into @col
end
close colcur
deallocate colcur
select @cols = substring(@cols, 3, datalength(@cols))
--select @cols
declare @sql varchar(4000)
declare @colname varchar(100),
@coltype varchar(30)
select @sql = 'select replace(''insert ' + @table + ' (' + @cols + ') '
select @sql = @sql + 'values ('''
declare ccur
cursor for
select column_name, data_type
from information_schema.columns
where table_name=@table
open ccur
fetch from ccur into @colname, @coltype
while @@fetch_status=0
begin
if @coltype in ('varchar', 'char', 'datetime')
select @sql=@sql + ''''''
select @sql=@sql + ' + coalesce(convert(varchar, ' + @colname + '), ''null'') + '
if @coltype in ('varchar', 'char', 'datetime')
select @sql=@sql + ''''''
select @sql = @sql + ''', '''
fetch from ccur into @colname, @coltype
end
close ccur
deallocate ccur
select @sql=substring(@sql, 1, datalength(@sql)-3)
select @sql=@sql + ')'', ''''''null'''''', ''null'') from ' + @table
exec (@sql)
1.23. OVERVIEW SQL SERVER VERSIONS AND BUILDS version 7 - 2012:
Sometimes it can be handy to have a simple listing of all SQL Server versions and builds.
You can find that list using the following link:
List of SQL Server versions (html file)
1.24. LOGGING FROM STORED PROCEDURES AND TSQL:
Often, you want to run a stored procedure, or just a TSQL block, and afterwards you want to view
some logging of this procedure, in order to see if that code ran succesfully or not.
There are many ways to get errors or actions logged.
For example, to a file, the screen (when you run it "now"), or a table, or the Windows application log etc...
Personally, I think that logging in a sort of Admin table is very effective.
And its easy: it's just an INSERT statement.
let's view a couple of examples.
Suppose we have a stored procedure which uses an input variable. Suppose this procedure takes as input
the path to a file (like E:\UPLOAD\DATA), and then performs some sort of processing, using that file.
So, the start of the sp code could be something like this:
CREATE PROCEDURE usp_processfile
@FILE_PATH VARCHAR(128) -- the input var
AS
BEGIN
-- the rest of the code
Now, before you want to do any processing at all, you first want to check the "validity" of this
input variable. So, you want to know first that it's not NULL, and that it at least contains a "/" character somewhere,
which makes it likely that the input variable (which a user has typed in, or the procedure received from elsewhere),
is indeed a "path". So, we might have put this test in the beginning of our sp:
if @FILE_PATH IS NULL or (charindex('\', @FILE_PATH) = 0)
BEGIN
  SET @ERR_MESSAGE='Incorrect file path.'
  GOTO error_section
END
Now let's take a look at a few examples on how we can log that errormessage.
Logging to a table:
error_section:
  INSERT INTO ADMINTABLE
  VALUES
  (getdate(),'usp_processfile',@ERR_MESSAGE)
  PRINT @ERR_MESSAGE -- show to the screen as well
RETURN -- terminate procedure
Raising an error to a calling application:
error_section:
RAISERROR (@ERR_MESSAGE, 10, 1)
RETURN -- terminate procedure
Raising an error to a calling application and write an event in the SQL Server log and Windows eventlog:
error_section:
RAISERROR (@ERR_MESSAGE, 10, 1) with log
RETURN -- terminate procedure
Logging to a file:
error_section:
DECLARE @log_cmd varchar(128)
SELECT @log_cmd='echo'+' '+@ERR_MESSAGE+' >> C:\MANAGEMENT\LOAD_FILE.LOG'
EXEC master.dbo.xp_cmdshell @log_cmd
RETURN -- terminate procedure
1.25. USING "EXEC" IN TSQL AND STORED PROCEDURES:
Important: if you use "exec" (or sp_executesql) to dynamically create SQL from strings and variables,
and if one or more variables comes from user input, then there exists the potential danger of "SQL injection".
This could mean that the resulting code could be something that you most definitely do not want to execute,
and it could even be dangerous.
This is especially true for code that's exposed through public sites, like in a backend database for a website.
So always check the variables very carefully.
Usage 1:
In TSQL code, like TSQL batches and stored procedures, you might encounter the "exec" keyword.
You probably have used it many times already, since if you want to execute a stored procedure from a Query Window,
you would use:
exec StoredProcedureName
So, if you want to execute existing code in for example stored procedures, extended stored procedures
(which could be stored in a module or .dll), you simply call them using "exec".
This is very easy indeed. Here are a few other examples:
exec StoredProcedureName -- calling a regular sp from a query tool
exec xp_ExtendedStoredProcedureName -- calling an extended sp from a query tool
Now let's try this:
CREATE procedure usp_proc1 @input1 varchar(32)
AS
BEGIN
PRINT 'Hi from usp_proc1'
PRINT @input1
END
GO
CREATE procedure usp_proc2 @input2 varchar(32)
AS
BEGIN
PRINT 'Hi from usp_proc2'
PRINT @input2
END
GO
CREATE procedure usp_proc3 @input3 varchar(32)
AS
BEGIN
PRINT 'Hi from usp_proc3'
PRINT @input3
END
GO
Now let's create a Main routine, that calls all 3 upper sp's: