Getting the last inserted value using SCOPE_IDENTITY()

by Kevin Bosch 13. August 2010 10:09

In SQL server the are three ways to get the latest value of an Identity Record

  1. SELECT @@IDENTITY
  2. SELECT IDENT_CURRENT(‘tablename’)
  3. SELECT SCOPE_IDENTITY()

SELECT @@IDENTITY

This will always return the last IDENTITY value produced on a given connection, regardless of the table that produced the value, and regardless of the scope of the statement that produced the value. While @@IDENTITY is limited to the current session, it is not limited to the current scope. For example if a trigger is added to a table to do auditing and that audit table has a Identity Column the value of the @@IDENTITY will contain the identity value for the trigger table and not the table within the codes scope. This is because the the trigger insert fired after the table insert.

SELECT IDENT_CURRENT(‘tablename’)

This statement will return the last IDENTITY value produced in a table, regardless of the connection that created the value, and regardless of the scope of the statement that produced the value. This is very seldom used as IDENT_CURRENT is only limited to a specified table and can cause havoc in a multi user environment if relied on. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope.

SELECT SCOPE_IDENTITY()

It returns the last IDENTITY value produced on a connection and by a statement in the same scope, regardless of the table that produced the value. like @@IDENTITY it will will return the last identity value created in the current session, however the but the SCOPE_IDENTITY() will also be limit it to your current scope. When inserting into a table it will only return the identity for that table and will not return and other inserts any triggers could have caused. This is typically what is expected when authoring code.

So when considering @@IDENTITY vs SCOPE_IDENTITY() always go with SCOPE_IDENTITY(). Using @@IDENTITY is opening up potential problems when triggers which involve identity columns are subsequently added to tables.  Using SCOPE_IDENTITY() will always return the the expected value in the context of the last insert statement. and will protect your code from any triggers added at a later date.

 

 

Tags:

SQL development

DB reindex script to keep the DB in autopilot mode

by Kevin Bosch 10. August 2010 20:43

Part of SQL db maintenance is rebuilding indexes in the databases. SQL Server automatically maintains indexes whenever insert, update, or delete operations are made to the underlying data however over time these modifications can cause the information in the index to become fragmented. When fragmentation exists when indexes have pages in which the logical ordering, based on the key value, does not match the physical ordering inside the data file. Fragmented indexes can affect query performance. The script below is a sledge hammer approach to re indexing all indexes in all tables in all databases on a given SQL instance. With smaller type databases <10 GB this approach is effective in keeping the database in auto pilot mode. Good DBA’s will raise their eye brows at this however there are lots of smaller sites which don’t have the luxury of full time DBA’s. This script will work on sql 2005+

DECLARE @Database VARCHAR(255)   
DECLARE @Table VARCHAR(255)  
DECLARE @cmd NVARCHAR(500)  
DECLARE @fillfactor INT 

SET @fillfactor = 90 

DECLARE DatabaseCursor CURSOR FOR  
SELECT name FROM master.dbo.sysdatabases   
WHERE name NOT IN ('master','model','msdb','tempdb','distrbution')   
ORDER BY 1  

OPEN DatabaseCursor  

FETCH NEXT FROM DatabaseCursor INTO @Database  
WHILE @@FETCH_STATUS = 0  
BEGIN  

   SET @cmd = 'DECLARE TableCursor CURSOR FOR SELECT table_catalog + ''.'' + table_schema + ''.'' + table_name as tableName   
                    FROM ' + @Database + '.INFORMATION_SCHEMA.TABLES WHERE table_type = ''BASE TABLE'''   

   -- create table cursor  
   EXEC (@cmd)  
   OPEN TableCursor   

   FETCH NEXT FROM TableCursor INTO @Table   
   WHILE @@FETCH_STATUS = 0   
   BEGIN   

       SET @cmd = 'ALTER INDEX ALL ON ' + @Table + ' REBUILD WITH (FILLFACTOR = ' + CONVERT(VARCHAR(3),@fillfactor) + ')'  
       print  @cmd  -- print cmd to see what it is doing. 
       EXEC (@cmd) 
       
       FETCH NEXT FROM TableCursor INTO @Table   
   END   

   CLOSE TableCursor   
   DEALLOCATE TableCursor  

   FETCH NEXT FROM DatabaseCursor INTO @Database  
END  
CLOSE DatabaseCursor   
DEALLOCATE DatabaseCursor  

Tags: ,

SQL development

Simple backup script for SQL server databases

by Kevin Bosch 9. August 2010 21:26

The script below provides a very simple procedure to backup all databases except for the specified ones on a given SQL server instance. By design it is built with the idea of a simple recovery model been used. You can wrap it up as a procedure and execute it on SQL Express. In databases using the simple recovery model, you may restore full or differential backups only. It is not possible to restore a database backed up with simple recovery mode to a given point in time, you may only restore it to the exact time when a full or differential backup occurred. So if that is an acceptable recovery model then this script will be useful. When it executes it will create a full backup of the databases overriding any given files of the same name. The retention variable allows a simple but very effective method for defining how many backups to keep. The default Retention given in the script is day of week allowing retention of 7 days.

DECLARE @Database varchar(128)   
DECLARE @cmd nvarchar(1024)
DECLARE @BackupLocation varchar(128)
DECLARE @RetentionVariable varchar(32)

-- backup path eg c:\backups\
Select @BackupLocation = 'C:\'
-- the Retention Variable to use default to day of week ie keeping 7 days backups in the backup location
Select @RetentionVariable = DATENAME(dw, getdate())
--Select @RetentionVariable = DATENAME(d, getdate()) -- to keep 1-31 backups depending on the month
--Select @RetentionVariable = DATENAME(dy, getdate()) -- to keep 364-365 backups depending on the year 
--Select @RetentionVariable = "" -- to keep only a single backup


DECLARE DatabaseCursor CURSOR FOR  
SELECT name FROM master.dbo.sysdatabases   
WHERE name NOT IN ('model','tempdb','distrbution') -- <-- these are the databases not to backup.    
ORDER BY 1  

OPEN DatabaseCursor  
FETCH NEXT FROM DatabaseCursor INTO @Database  
WHILE @@FETCH_STATUS = 0  
BEGIN  
   SET @cmd = 'BACKUP DATABASE ' + @Database + '
        TO  DISK = N'''+@BackupLocation +'' + @Database + '-' + @RetentionVariable + '.BAK'' WITH NOFORMAT, INIT,  
            NAME = N''' + @Database + '-Full Database Backup'', SKIP, NOREWIND, NOUNLOAD,  STATS = 10'  
    --print @cmd
    EXEC (@cmd) 
    
    SET @cmd = 'declare @backupSetId as int
    select @backupSetId = position 
        from msdb..backupset 
            where database_name=N'''+ @Database +''' 
            and backup_set_id=(select max(backup_set_id) 
                                from msdb..backupset 
                                where database_name=N'''+ @Database+ ''')

    if @backupSetId is null 
        begin 
            raiserror(N''Verify failed. Backup information for database '''''+@Database+''''' not found.'', 16, 1) 
        end

    RESTORE VERIFYONLY 
        FROM DISK = N''' + @BackupLocation + @Database+ '-' + @RetentionVariable + '.BAK'' 
        WITH  FILE = @backupSetId,  NOUNLOAD,  NOREWIND'
    -- print @cmd
    EXEC (@cmd) 

   FETCH NEXT FROM DatabaseCursor INTO @Database  
END  
CLOSE DatabaseCursor   
DEALLOCATE DatabaseCursor  

Tags: ,

SQL development

Parsing an Array of Data in SQL

by Kevin Bosch 7. February 2010 15:44

From time to time there is a need to convert a delimited list into a SQL table for comparison. The classic example is passing a parameter which contains a set of values to SQL. For example get me all products in categories 1,2,5,6,8. With Dynamic SQL this is easy as you can simply use a nested in TSQL statement

For Example:

Select * 
From product Table
Where categoryid in (1,2,5,6,8)

Now what happens if you are required or want to you use stored procedures. In this case the following will not work

Create procedure ExampleProc @categorylist  varchar (max) as
BEGIN
Select * 
From productTable
Where categoryid in @categorylist
END

Exec ExampleProc ‘1,2,5,6,8’  -- will not work. 

To solve this we can make SQL server convert a list into a table using the function below:

Create   Function fn_ParseIntArray  (@Array varchar(8000), @separator char(1))
RETURNS @ParseIntArray table (i int)
AS
BEGIN
-- Created by Kevin Bosch
-- Date 16-Nov-2002
-- Part Of the Code Associate Codebase for SQL 
--      ©2003 Code Associate 
-- do not copy without permission
-- @Array is the array we wish to parse
-- @Separator is the separator charactor such as a comma
declare @separator_position int -- This is used to locate each separator character
-- For the loop to work an extra separator at the end is needed.  The Code will always
-- look to the left of the separator character for each array value
set @array = @array + @separator
-- Loop through the string searching for separtor characters
while patindex('%' + @separator + '%' , @array) <> 0 
begin
  -- patindex matches the a pattern against a string
  select @separator_position =  patindex('%' + @separator + '%' , @array)
  -- This is where the code inserts the left most value in the array into the table 
  -- the value will be cast as an integer when going into the table if it is not a valid 
  -- integer it will raise an exception
 if len(@array) > 1
  insert into @ParseIntArray values (left(@array, @separator_position - 1))
  -- This replaces what we just processed with and empty string
  select @array = stuff(@array, 1, @separator_position, '')
end
RETURN
END

Example of usage.

Create procedure ExampleProc2 @categorylist varchar (max) AS BEGIN Select * From productTable Where categoryid in dbo.fn_ParseIntArray(@categorylist, ‘,’) END
Exec ExampleProc2 ‘1,2,5,6,8’  -- will work. 

 

Now SQL server will take the ‘1,2,5,6,8’ and convert it into a table array of integers which it can then use in a nested statement. Whilst this is useful and quick it is not the most optimal method for doing this. A .NET function written and deployed in SQL will outperform this method, or the use of dynamic SQL should be considered.

Tags: ,

SQL development

Calendar

<<  February 2012  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

View posts in large calendar
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012 Code Associate