Error converting data type varchar to numeric in standard audit trigger - triggers

I have been using a standard block of TSQL for auditing of various tables for some time now. However I now have a problem when running the trigger on a new table: "Error converting data type varchar to numeric". This occurs when running the EXEC (#sql) line. I've determined that the code for #sql is:
insert Audit_AppointmentsWS
(Type,
TableName,
PK,
FieldName,
OldValue,
NewValue,
UpdateDate,
UserName)
SELECT 'U',
'AppointmentsWorkshop',
+convert(varchar(100), coalesce(i.UniqueID,d.UniqueID)),
'[JobHours]',
convert(varchar(1000),d.[JobHours]),
convert(varchar(1000),i.[JobHours]),
'20220816 12:32:43:410',
'DELLXPS\ian'
from #ins i full outer join #del d on i.UniqueID = d.UniqueID where ISNULL(i.JobHours],'') <> ISNULL(d.[JobHours],'')
I've tried deleting the trigger & the audit table and then recreating them but no joy. I've also tried copying an existing trigger and just changing the table details but I still get the same error. I'm completely stumped on this and would appreciate some feedback. Many thanks in advance!
Here is the trigger:
/****** Object: Trigger [dbo].[tr_AppointmentsWS] Script Date: 16/08/2022 12:02:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create TRIGGER [dbo].[tr_AppointmentsWS] ON [dbo].AppointmentsWorkshop FOR UPDATE, DELETE
AS
DECLARE #bit INT ,
#field INT ,
#maxfield INT ,
#char INT ,
#fieldname VARCHAR(128) ,
#TableName VARCHAR(128) ,
#AuditTable VARCHAR(128) ,
#PKCols VARCHAR(MAX) ,
#sql VARCHAR(2000),
#UpdateDate VARCHAR(21) ,
#UserName VARCHAR(128) ,
#Type CHAR(1) ,
#PKSelect VARCHAR(MAX)
--Changes required:
-- 1. Change the name of the trigger and the table, above
-- 2. Change #TableName to match the table to be audited
-- 3. Change the #AuditTable to the table holding the changes
SELECT #TableName = 'AppointmentsWorkshop'
SELECT #AuditTable = 'Audit_AppointmentsWS'
-- date and user
SELECT #UserName = SYSTEM_USER ,
#UpdateDate = CONVERT(VARCHAR(8), GETDATE(), 112) + ' ' + CONVERT(VARCHAR(12), GETDATE(), 114)
-- Action
IF EXISTS (SELECT * FROM inserted)
IF EXISTS (SELECT * FROM deleted)
SELECT #Type = 'U'
ELSE
SELECT #Type = 'I'
ELSE
SELECT #Type = 'D'
-- get list of columns
SELECT * INTO #ins FROM inserted
SELECT * INTO #del FROM deleted
-- Get primary key columns for full outer join
SELECT #PKCols = COALESCE(#PKCols + ' and', ' on') + ' i.' + c.COLUMN_NAME + ' = d.' + c.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk, INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE pk.TABLE_NAME = #TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
-- Get primary key select for insert
SELECT #PKSelect = COALESCE(#PKSelect+'+','') + '+convert(varchar(100), coalesce(i.' + COLUMN_NAME +',d.' + COLUMN_NAME + '))'
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk, INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE pk.TABLE_NAME = #TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
IF #PKCols IS NULL
BEGIN
RAISERROR('no PK on table %s', 16, -1, #TableName)
RETURN
END
SELECT #field = 0, #maxfield = MAX(COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID'))
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #TableName
WHILE #field < #maxfield
BEGIN
SELECT #field = MIN(COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID'))
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
AND COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID') > #field
SELECT #bit = (#field - 1 )% 8 + 1
SELECT #bit = POWER(2,#bit - 1)
SELECT #char = ((#field - 1) / 8) + 1
IF SUBSTRING(COLUMNS_UPDATED(),#char, 1) & #bit > 0 OR #Type IN ('I','D')
BEGIN
SELECT #fieldname = COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
AND COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + #Tablename),COLUMN_NAME, 'ColumnID') = #field
SELECT #sql = 'insert ' + #AuditTable + '
(Type,
TableName,
PK,
FieldName,
OldValue,
NewValue,
UpdateDate,
UserName)
SELECT ''' + #Type + ''','''
+ #TableName + ''',' + #PKSelect
+ ',''[' + #fieldname + ']'''
+ ',convert(varchar(1000),d.[' + #fieldname + '])'
+ ',convert(varchar(1000),i.[' + #fieldname + '])'
+ ',''' + #UpdateDate + ''''
+ ',''' + #UserName + ''''
+ ' from #ins i full outer join #del d'
+ #PKCols
+ ' where ISNULL(i.[' + #fieldname + '],'''') <> ISNULL(d.[' + #fieldname + '],'''')' --Skip identical values and excludes NULLS vs empty strings
EXEC (#sql)
END
END

Well I finally figured it out. The error is being generated with columns of data type 'decimal' and it is down to the ISNULL section of the last SELECT. I've fixed it by checking for the decimal type and then using the following code (which included a zero rather than an empty sting):
+ ' where ISNULL(i.[' + #fieldname + '],''0'') <> ISNULL(d.[' + #fieldname + '],''0'')' --Skip identical values and excludes NULLS vs empty strings

Related

Dynamic sql to select a specific value from a column using Joins

I am attempting to use dynamic sql to select a value based on a field. I have a table of field references I am using for the column names. What I am having troubles with is of course the dynamic sql. My return result is (SELECT ecoa_code FROM CRA_METRO2_BASE WHERE id = 568470) for example. But I really want it to run that select statement. Executing only returns the last row.
DECLARE #BaseCol VARCHAR(250)
SELECT
#BaseCol = '(SELECT ' + FR_base.field_name + ' FROM CRA_METRO2_BASE WHERE id = ' + CONVERT(VARCHAR(15), B.id) + ')'
FROM CRA_INNOVIS_AUDIT_ERROR_FIELDS E
LEFT JOIN CRA_METRO2_BASE B
ON B.id = E.base_id
LEFT JOIN CRA_METRO2_FIELD_REF FR_base
ON FR_base.id = E.base_field_ref
WHERE E.audit_id = #audit_id
EXEC(#BaseCol)
I am not sure I understand your premises correctly and without a mock-up...; so please take this answer with a grain of salt:)
DECLARE #sqlstring VARCHAR(MAX)
SELECT #sqlstring = 'SELECT ' + a.column_name + ' FROM ' + a.[Schema] + '.' + a.table_name
from (
SELECT TOP 1 T.object_id,OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name], AC.[name] AS [column_name]
--,TY.[name] AS system_data_type
, AC.[max_length],
AC.[precision], AC.[scale], AC.[is_nullable], AC.[is_ansi_padded]
,AC.column_id
FROM sys.tables AS T
INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
) a
SELECT #sqlstring
EXEC(#sqlstring)
So I used my above query and now I am using a CTE to build my basic result list. And in my cte I create update statements which then are all put into a temp table.
I extract the update statements and execute them on the temp table. And walla, I have my results!
IF(OBJECT_ID('tempdb..#Temp') IS NOT NULL)
BEGIN
DROP TABLE #Temp
END
CREATE TABLE #Temp
(
usb_data VARCHAR(500),
cra_data VARCHAR(500)
);
WITH ErrorFieldsCTE(id, field, usb_data, cra_data, AUD, SOR, acceptable_variance, is_variance_known, is_reviewed)
AS(
SELECT
+ 'UPDATE #TEMP SET usb_data = (SELECT ' + FR_base.field_name +' FROM CRA_METRO2_BASE WHERE id = '+ CONVERT(VARCHAR(25), B.id) +' ) WHERE id = ' + CONVERT(VARCHAR(15), E.id) + ' ' [usb_data],
+ 'UPDATE #TEMP SET cra_data = (SELECT ' + FR_audit.field_name +' FROM CRA_INNOVIS_INBOUND_AUDIT_INFORMATION WHERE id = '+ CONVERT(VARCHAR(25), A.id) +') WHERE id = ' + CONVERT(VARCHAR(15), E.id) + ' ' [cra_data]
FROM CRA_INNOVIS_AUDIT_ERROR_FIELDS E
LEFT JOIN CRA_METRO2_BASE B
ON B.id = E.base_id
LEFT JOIN CRA_INNOVIS_INBOUND_AUDIT_INFORMATION A
ON A.id = E.audit_id
LEFT JOIN CRA_METRO2_FIELD_REF FR_audit
ON FR_audit.id = E.audit_field_ref
LEFT JOIN CRA_METRO2_FIELD_REF FR_base
ON FR_base.id = E.base_field_ref
WHERE E.audit_id = #audit_id
)
INSERT INTO #Temp
SELECT
id, field, usb_data, cra_data, AUD, SOR, acceptable_variance, is_variance_known, is_reviewed
FROM ErrorFieldsCTE
SELECT -- extract query
#usb_data += usb_data + '',
#cra_data += cra_data + ''
FROM #Temp
EXEC(#usb_data) -- updating temp table, selects usb-data
EXEC(#cra_data) -- updating temp table, selects cra-data
SELECT -- return to web
id, field, usb_data, cra_data, AUD, SOR, acceptable_variance, is_variance_known, is_reviewed
FROM #Temp
IF(OBJECT_ID('tempdb..#Temp') IS NOT NULL)
Begin
Drop Table #Temp
End

Rework query to include the Schema in the name

I am leeching off this post: Query to list number of records in each table in a database
With this procedure:
CREATE PROCEDURE ListTableRowCounts
AS
BEGIN
SET NOCOUNT ON
CREATE TABLE #TableCounts
(
TableName VARCHAR(500),
CountOf INT
)
INSERT #TableCounts
EXEC sp_msForEachTable
'SELECT PARSENAME(''?'', 1),
COUNT(*) FROM ? WITH (NOLOCK)'
SELECT TableName , CountOf
FROM #TableCounts
ORDER BY TableName
DROP TABLE #TableCounts
END
GO
The procedure works well enough but I need it to output the name as Schema.Name and sort by that.
Is that possible? I'm not sure how to change this but you can see what it is doing below:
I have several instances were the table names are the same from different schemas.
CREATE PROCEDURE ListTableRowCounts
AS
BEGIN
SET NOCOUNT ON
CREATE TABLE #TableCounts
( SchemaName VARCHAR(500),
TableName VARCHAR(500),
CountOf INT
)
INSERT #TableCounts
EXEC sp_msForEachTable
'SELECT PARSENAME(''?'', 2), PARSENAME(''?'', 1),
COUNT(*) FROM ? WITH (NOLOCK)'
SELECT SchemaName, TableName , CountOf
FROM #TableCounts
ORDER BY TableName, SchemaName
DROP TABLE #TableCounts
END
GO
Taking some code from: https://stackoverflow.com/a/1443723/4584335
and from: How do I list all tables in all databases in SQL Server in a single result set?
Could I suggest this one (just in case "sp_msForEachTable" doesn't exist anymore):
declare #sql nvarchar(max);
select #sql = isnull(#sql + N'union all ', '')
+ N'
select b.name as "DB"
,a.name collate Latin1_General_CI_AI
,a.object_id
,a.schema_id
,' + cast(database_id as nvarchar(10)) + N'
,p.[Rows]
from ' + quotename(name) + N'.sys.tables a
join
' + quotename(name) + N'.sys.indexes i
on a.OBJECT_ID = i.object_id
and i.index_id <= 1
join
' + quotename(name) + N'.sys.partitions p
on i.object_id = p.OBJECT_ID
and i.index_id = p.index_id
join sys.databases b
on database_id=' + cast(database_id as nvarchar(10)) + ' '
from sys.databases
where state = 0
and user_access = 0;
exec sp_executesql #sql;

Dynamically generated insert and select statements

I have a database with some tables in it and I want to make dynamically generated insert and select statements without the need to use the case statement in the select clause for each table.
The select statement is quite easy, the challenge is with the insert one because I have to deal with each column and its data type. I managed to overcome it by means of a case statement, but I think it's hard working for tables with lots of columns and for databases with many tables.
I wish it was possible to change hardcoded table and column names for each table and column I need the dynamically generated SQL command.
I worked out in the following select statement for the given tables of the database (testDB) I have:
use testDB;
go
set dateformat dmy;
select
'select * from ' + s.name + '.' + t.name + ';' as cmd_select,
'insert into ' + s.name + '.' + t.name + ' (' +
stuff(( select ', ' + column_name
from information_schema.columns
where table_name = t.name and ordinal_position > 1
order by ordinal_position
for xml path(''), type).value('.', 'nvarchar(max)'),
1, 2, '')
+ ') values (' +
case t.name
when 'Person' then '''xxx'''
when 'WeightHistory' then '0.0, ''' + convert(varchar, current_timestamp, 103) + ''', ''' + left(convert(varchar, current_timestamp, 108), 5) + ''', 1'
when 'WorkTime' then '''' + convert(varchar, current_timestamp, 103) + ''', ''' + left(convert(varchar, current_timestamp, 108), 5) + ''', 1, null'
when 'TimeReference' then '''07:00'', ''' + convert(varchar, current_timestamp, 103) + ''', null'
end
+ ');'as cmd_insert --,
--t.lob_data_space_id
--, s.name, t.name, *
from sys.tables as t
inner join sys.schemas as s on t.schema_id = s.schema_id
where t.lob_data_space_id = 0; /* tables that don't have LOB columns (sysdiagrams, varchar(max), xml, etc.) */
What exactly I want to know is:
Is there a better way of making the dynamically generated insert statement without using a case statement for each table and column of the database?
ADITIONAL INFO
The table definition for the above code is the following:
if not exists (select * from sys.tables where lower(name) = N'person' )
begin
create table Person.Person (
PersonID int
constraint PK_Person
primary key
identity (1, 1),
Name varchar(100)
);
end;
go
if not exists (select * from sys.tables where lower(name) = N'weighthistory' )
begin
create table dbo.WeightHistory (
WeightHistoryID int
constraint PK_WeightHistory
primary key
identity (1, 1),
MeasureValue money,
MeasureDate date,
MeasureTime time(0),
PersonID int,
constraint FK_Weight_Person foreign key (PersonID) references Person.Person (PersonID)
);
end;
go
if not exists (select * from sys.tables where lower(name) = N'worktime')
begin
create table WorkTime (
WorkTimeID int
constraint PK_WorkTime primary key
identity(1, 1),
WorkDate date,
WorkTime time(0),
PersonID int,
TimeReferenceID int,
constraint FK_WorkTime_Person foreign key (PersonID) references Person.Person (PersonID) on delete cascade,
constraint FK_WorkTime_Reference foreign key (TimeReferenceID) references Work.Timereference (TimeReferenceID)
);
end;
go
if not exists (select * from sys.tables where lower(name) = N'timereference')
begin
create table Work.TimeReference (
TimeReferenceID int
constraint PK_TimeReferene primary key
identity (1, 1),
WorkTime time(0),
WorkTimeStartDate date,
WorkTimeEndDate date
);
end;

Change nulls to zeros (pivot table)

I have created stored procedure which creates pivoted table as below. The problem is that it doesn't work as expected because it doesn't change nulls to zeros. Could You please show me the right direction?
create procedure sp_system_counts
as
declare #columns nvarchar(max)
declare #columnscondition nvarchar(max)
declare #query nvarchar(max)
select distinct systemgroup, systemgroupsortorder into #temp_table_system_group
from systemgroups
where systemgroup not like 'N/A'
order by systemgroupsortorder
select #columns = isnull(#columns + ',', '') + '[' + convert(varchar, systemgroup) + ']' FROM #temp_table_system_group
select #columnscondition = + isnull(#columnscondition + ' or ', '') + '[' + convert(varchar, systemgroup) + '] <> 0' FROM #temp_table_system_group
--select #columns
create table #temp_systems (
systemid int,
systemnane varchar(max),
region varchar(50),
systemgroup varchar(50),
remsid int,
remscode int)
insert into #temp_systems
select distinct sy.systemid, sy.systemname, co.region, syg.systemgroup, re.remsid, re.remscode
from systems sy
inner join servers se on sy.systemid = se.systemid and sy.systemid = sy.systemid
inner join rems re on se.remsid = re.remsid
inner join cities ci on re.cityid = ci.cityid
inner join countries co on ci.countryid = co.countryid
inner join systemmodels sym on sy.systemmodelid = sym.systemmodelid
inner join systemtypes syt on sym.systemtypeid = syt.systemtypeid
inner join systemgroups syg on syt.systemgroupid = syg.systemgroupid
where syg.systemgroup not like 'N/A'
order by syg.systemgroup
set #query = '
select region, ' + #columns + '
from (
select distinct region, systemgroup, cnt = isnull(count(systemid),0) from #temp_systems
group by region, systemgroup
with rollup) p
pivot (sum (cnt) for systemgroup in (' + #columns + ')) as asd
where (' + #columnscondition +')'
execute(#query)
drop table #temp_table_system_group
drop table #temp_systems
I'm not sure, about logic implemented here, but may be it will help, if you change:
select #columns = isnull(#columns + ',', '') + '[' + convert(varchar, systemgroup) + ']' FROM #temp_table_system_group
select #columnscondition = + isnull(#columnscondition + ' or ', '') + '[' + convert(varchar, systemgroup) + '] <> 0' FROM #temp_table_system_group
to
select #columns = isnull(#columns + ',', '') + 'isnull(' + quotename(convert(varchar, systemgroup)) + ', 0) as ' + quotename(convert(varchar, systemgroup)) FROM #temp_table_system_group
select #columnscondition = + isnull(#columnscondition + ' or ', '') + 'isnull(' + quotename(convert(varchar, systemgroup)) + ', 0) <> 0' FROM #temp_table_system_group
i.e. doing isnull(.., 0) over pivoted data, not before pivoting

tsql to generate index creation text, then evaluate it

I have a script that creates and populates a bunch of tables from data we receive as
text files. After loading the data into sql server 2008, I want to add indexes. The cut-and-paste-created script I'm using is shown below.
Is
there a way in TSQL to create a function that will generate and then
evaluate the DDL commands? The function would take parameters
of table name, column name(s) and clustered/not. If there would have
to be a separate function for 1 column/2 column indexes, I'd still
be interested.
I'm envisioning something like:
hypotheticalFunction('clustered', 'precCdaEarn', 'account', 'seq')
hypothericalFunction('nonClustered', 'flats', 'vendorAcct')
and that would check if an acct called "idx_acct_seq" exists on table
precCdaEarn, and drop it if so, then create it as clustered index. (dropping it just
in case i change the definition). And create/recreate a nonclustered index on dbo.flats named "idx_vendorAcct"
========= this is what have now using cut & paste, apologies for the erratic spacing ==============
IF EXISTS (Select 'X'
FROM sysindexes
WHERE id = (SELECT OBJECT_ID('precCdaEarn'))
and name = 'idx_account_seq')
DROP index precCdaEarn.idx_account_seq
Create nonclustered index idx_account_seq
ON precCdaEarn(account, seq)
;
IF EXISTS (Select 'X'
FROM sysindexes
WHERE id = (SELECT OBJECT_ID('acct'))
and name = 'idx_c_precAcct_precMod')
DROP index acct.idx_c_precAcct_precMod
CREATE CLUSTERED INDEX index idx_c_precAcct_precMod
ON acct(precAcct, precMod)
AFAIK you cannot create a function for this, but with a stored procedure it's no problems.
Something like:
IF(OBJECT_ID('PRC_CREATE_INDEX', N'P') IS NOT NULL)
DROP PROCEDURE PRC_CREATE_INDEX
GO
CREATE PROCEDURE dbo.PRC_CREATE_INDEX
(
-- Table name to add index to
#pTablename AS VARCHAR(120)
-- Index name to add/change
, #pIndexname AS VARCHAR(120)
-- UNIQUE, CLUSTERED or other definition
, #pNewIndexDef AS VARCHAR(255) = ''
-- Columns in index
, #pNewIndexCols AS VARCHAR(255)
-- Recreate even if same
, #pForceRecreate AS SMALLINT = 0
-- Additional columns for INCLUDE
, #pColumnsInclude AS VARCHAR(255) = ''
)
AS
------------------------------------
-- Variabels
------------------------------------
DECLARE #SQL_DROP AS NVARCHAR(1000)
, #SQL_CREATE AS NVARCHAR(1000)
, #retval AS INTEGER
, #rowcount AS INTEGER
DECLARE #oldDescription AS VARCHAR(255)
, #newUnique AS SMALLINT
, #newClustered AS SMALLINT
, #newIndexDef AS VARCHAR(255)
, #newDescription AS VARCHAR(255)
, #drop_index AS BIT
, #temp AS VARCHAR(MAX)
------------------------------------
-- Initiate
------------------------------------
SET XACT_ABORT ON
SET NOCOUNT ON
-- Table exists?
IF(OBJECT_ID(#pTablename) IS NULL)
BEGIN
PRINT 'Error: Table does not exist: ' + #pTablename +
' (' + 'creation of index ''' + #pTablename + '.' + #pIndexname + ''')'
RETURN (1)
END
-----------------------------------------------------------------------------------------------------
-- New index
-----------------------------------------------------------------------------------------------------
-- Initiate (Remove blanks, get unique/clustered info)
SELECT #newIndexDef = #pNewIndexDef
-- Remove NONCLUSTERED
, #newIndexDef = REPLACE(#newIndexDef, 'nonclustered', '')
-- CLUSTERED?
, #newClustered = CASE WHEN PATINDEX('%clustered%', LOWER(#newIndexDef)) > 0 THEN 1 ELSE 0 END
, #newIndexDef = REPLACE(#newIndexDef, 'clustered', '')
-- UNIQUE?
, #newUnique = CASE WHEN PATINDEX('%unique%', LOWER(#pNewIndexDef)) > 0 THEN 1 ELSE 0 END
, #newIndexDef = REPLACE(#newIndexDef, 'unique', '')
-- Remove blanks etc.
, #pNewIndexCols = REPLACE(#pNewIndexCols, ' DESC', '#DESC')
, #newIndexDef = REPLACE(#newIndexDef, ' ', '')
, #newIndexDef = REPLACE(#newIndexDef, ',', '')
, #pNewIndexCols = REPLACE(#pNewIndexCols, ' ', '')
, #pColumnsInclude = REPLACE(#pColumnsInclude, ' ', '')
-- Correct defintion?
IF LEN(#newIndexDef) > 0
BEGIN
RAISERROR ('Index defintion ''%s'' is incorrect for index ''%s.%s''!', 11, 11, #pNewIndexDef, #pTablename, #pIndexname)
RETURN (1)
END
IF #newClustered = 1 AND LEN(#pColumnsInclude) > 0
BEGIN
RAISERROR ('Cannot specify included columns for a clustered index (''%s.%s'')!', 11, 11, #pIndexname, #pTablename)
RETURN (1)
END
IF #pNewIndexCols LIKE '%#DESC%'
BEGIN
RAISERROR ('Descending sort direction not supported (''%s.%s'')!', 11, 11, #pTablename, #pIndexname)
RETURN (1)
END
IF #newClustered = 1 AND EXISTS
(
SELECT 1
FROM sys.indexes (NOLOCK)
WHERE object_id = OBJECT_ID(#pTablename, N'U')
AND name <> #pIndexname
AND type = 1 -- CLUSTERED
AND is_hypothetical = 0
)
BEGIN
SELECT #temp = name
FROM sys.indexes (NOLOCK)
WHERE object_id = OBJECT_ID(#pTablename, N'U')
AND name <> #pIndexname
AND type = 1 -- CLUSTERED
AND is_hypothetical = 0
RAISERROR ('Cannot add CLUSTERED index (''%s.%s'') - table already has a clustered index (''%s'')!', 11, 11, #pTablename, #pIndexname, #temp)
RETURN (1)
END
-- Prepare SQL
-- CREATE
SELECT #SQL_CREATE = ''
+ 'CREATE '
+ CASE WHEN #newUnique = 1 THEN 'UNIQUE ' ELSE '' END
+ CASE WHEN #newClustered = 1 THEN 'CLUSTERED ' ELSE 'NONCLUSTERED ' END
+ 'INDEX ' + #pIndexname + ' '
+ 'ON ' + #pTablename + ' '
+ '(' + #pNewIndexCols + ')'
+ CASE WHEN LEN(#pColumnsInclude) > 0 THEN ' INCLUDE (' + #pColumnsInclude + ')' ELSE '' END
-- Description
SELECT #newDescription = ''
+ CASE WHEN #newUnique = 1 THEN 'UNIQUE ' ELSE '' END
+ CASE WHEN #newClustered = 1 THEN 'CLUSTERED ' ELSE 'NONCLUSTERED ' END
+ '(' + #pNewIndexCols + ')'
+ CASE WHEN LEN(#pColumnsInclude) > 0 THEN ' INCLUDE (' + #pColumnsInclude + ')' ELSE '' END
-- DROP
SET #SQL_DROP = ''
+ 'DROP INDEX ' + #pTablename
+ '.' + #pIndexname
-----------------------------------------------------------------------------------------------------
-- Current index
-----------------------------------------------------------------------------------------------------
-- Initiate
SELECT #drop_index = 0
-- Get definition/description and check if recreation is needed
IF EXISTS
(
SELECT 1
FROM sys.indexes (NOLOCK)
WHERE object_id = OBJECT_ID(#pTablename, N'U')
AND name = #pIndexname
)
BEGIN
-- Description
SELECT #oldDescription = ''
+ CASE WHEN i.is_unique = 1 THEN 'UNIQUE ' ELSE '' END
+ CASE
WHEN i.type = 1 THEN 'CLUSTERED '
WHEN i.type = 2 THEN 'NONCLUSTERED '
ELSE '? '
END
+ '(' + STUFF(
(
SELECT ',' + COL_NAME(ic.object_id, ic.column_id)
FROM sys.index_columns ic (NOLOCK)
WHERE ic.object_id = i.object_id
AND ic.index_id = i.index_id
AND ic.is_included_column = 0
ORDER BY ic.key_ordinal
FOR XML PATH('')
)
, 1, 1, '') + ')'
+ ISNULL(' INCLUDE (' + STUFF(
(
SELECT ',' + COL_NAME(ic.object_id, ic.column_id)
FROM sys.index_columns ic (NOLOCK)
WHERE ic.object_id = i.object_id
AND ic.index_id = i.index_id
AND ic.is_included_column = 1
ORDER BY ic.index_column_id -- Or column_id???
FOR XML PATH('')
)
, 1, 1, '') + ')', '')
FROM sys.indexes i (NOLOCK)
WHERE i.object_id = OBJECT_ID(#pTablename, N'U')
AND i.name = #pIndexname
-- Exit?
-- If not changed and no force of recreation
IF #oldDescription = #newDescription
AND
#pForceRecreate = 0
BEGIN
RETURN 0
END
-- We should drop current index..
SET #drop_index = 1
END
-----------------------------------------------------------------------------------------------------
-- Execute SQL
-----------------------------------------------------------------------------------------------------
-- Exec
IF #drop_index = 1
EXEC sp_executesql #SQL_DROP
IF LEN(#pNewIndexCols) > 0
EXEC sp_executesql #SQL_CREATE
-- Message
IF LEN(#pNewIndexCols) > 0 AND #drop_index = 0
PRINT 'Created index ' + #pTablename + '.' + #pIndexname + ' (' + #newDescription + ')'
ELSE IF LEN(#pNewIndexCols) > 0 AND #drop_index = 1
PRINT 'Recreated index ' + #pTablename + '.' + #pIndexname + CHAR(10) +
' From: ' + #oldDescription + CHAR(10) +
' To: ' + #newDescription
-- Well, this will perhaps occur when and if this proc is changed...
ELSE IF #drop_index = 1
PRINT 'Removed index ' + #pTablename + '.' + #pIndexname + ' (' + #oldDescription + ')'
-----------------------------------------------------------------------------------------------------
-- Exit
-----------------------------------------------------------------------------------------------------
RETURN (0)
GO
Use it like:
EXEC dbo.PRC_CREATE_INDEX
#pTablename = 'someTable'
, #pIndexname = 'idx_someTable'
, #pNewIndexDef = 'CLUSTERED'
, #pNewIndexCols = 'some, columns, listed'