Error with Quotation marks in Dynamic TSQL - tsql

I tried many posts to resolve my error below, but I am stuck. Would appreciate if someone could guide me in the right direction here.
-- Without EXEC -- this works fine
DECLARE #BPPA AS nvarchar(5)
SET #BPPA = 'BPP2'
DECLARE #BPPB AS nvarchar(5)
SET #BPPB = 'BPP4'
SELECT T0.CustCode AS 'CustCode', MAX(T0.CustName) AS 'CustName', T0.ItemCode AS 'ItemCode', MAX(T0.ItemName) AS 'ItemName', MAX(T0.BPP) AS 'BPP',
SUM(T0.Qty) AS 'QtyP1', SUM(T0.SalesAmt) AS 'SalesAmtP1', 0 AS 'QtyP2', 0 AS 'SalesAmtP2'
FROM [dbo].[VWAJ_SALANALYSIS] T0
WHERE T0.DocDate BETWEEN '2016-01-01' AND '2016-03-31'
AND (
((',' + RTRIM(T0.BPP) + ',') LIKE '%,' + #BPPA + ',%') OR ((',' + RTRIM(T0.BPP) + ',') LIKE '%,' + #BPPB + ',%')
)
GROUP BY T0.CustCode, T0.ItemCode
-- With EXEC -- Error 'Must declare the scalar variable "#BPPA"
DECLARE #BPPA AS nvarchar(5)
SET #BPPA = 'BPP2'
DECLARE #BPPB AS nvarchar(5)
SET #BPPB = 'BPP4'
EXEC('
SELECT T0.CustCode AS ''CustCode'', MAX(T0.CustName) AS ''CustName'', T0.ItemCode AS ''ItemCode'', MAX(T0.ItemName) AS ''ItemName'', MAX(T0.BPP) AS ''BPP'',
SUM(T0.Qty) AS ''QtyP1'', SUM(T0.SalesAmt) AS ''SalesAmtP1'', 0 AS ''QtyP2'', 0 AS ''SalesAmtP2''
FROM [dbo].[VWAJ_SALANALYSIS] T0
WHERE T0.DocDate BETWEEN ''2016-01-01'' AND ''2016-03-31''
AND (
(('','' + RTRIM(T0.BPP) + '','') LIKE ''%,'' + #BPPA + '',%'') OR (('','' + RTRIM(T0.BPP) + '','') LIKE ''%,'' + #BPPB + '',%'')
)
GROUP BY T0.CustCode, T0.ItemCode
')
What am I doing wrong? I believe I am missing some quotation marks, but not sure how many and why.
Thanks in advance!
AJ

First of all, you could have read the error message, that should have given you a hint.
The quotation marks look ok, the main problem is that the scope for variables is the batch they are declared in. The EXEC statement executes the sql string in a separate batch, so your variables #BPPA and #BPPB can't be found in that batch. You have to split your string to take the values from the calling batch:
DECLARE #BPPA AS varchar(5) = 'BPP2'
DECLARE #BPPB AS varchar(5) = 'BPP4'
EXEC('
SELECT T0.CustCode AS ''CustCode'', MAX(T0.CustName) AS ''CustName'', T0.ItemCode AS ''ItemCode'', MAX(T0.ItemName) AS ''ItemName'', MAX(T0.BPP) AS ''BPP'',
SUM(T0.Qty) AS ''QtyP1'', SUM(T0.SalesAmt) AS ''SalesAmtP1'', 0 AS ''QtyP2'', 0 AS ''SalesAmtP2''
FROM [dbo].[VWAJ_SALANALYSIS] T0
WHERE T0.DocDate BETWEEN ''2016-01-01'' AND ''2016-03-31''
AND (
(('','' + RTRIM(T0.BPP) + '','') LIKE ''%,''' + #BPPA + ''',%'') OR (('','' + RTRIM(T0.BPP) + '','') LIKE ''%,''' + #BPPB + ''',%'')
)
GROUP BY T0.CustCode, T0.ItemCode
')
For the passing of parameters, you should have a look at the system stored procedure sp_executesql.
Example added:
Using sp_executesql, your EXEC statement could look like this:
EXEC sp_executesql N'
SELECT T0.CustCode AS ''CustCode'', MAX(T0.CustName) AS ''CustName'', T0.ItemCode AS ''ItemCode'', MAX(T0.ItemName) AS ''ItemName'', MAX(T0.BPP) AS ''BPP'',
SUM(T0.Qty) AS ''QtyP1'', SUM(T0.SalesAmt) AS ''SalesAmtP1'', 0 AS ''QtyP2'', 0 AS ''SalesAmtP2''
FROM [dbo].[VWAJ_SALANALYSIS] T0
WHERE T0.DocDate BETWEEN ''2016-01-01'' AND ''2016-03-31''
AND (
(('','' + RTRIM(T0.BPP) + '','') LIKE ''%,'' + #BPPA + '',%'') OR (('','' + RTRIM(T0.BPP) + '','') LIKE ''%,'' + #BPPB + '',%'')
)
GROUP BY T0.CustCode, T0.ItemCode
'
, N'#BPPA nvarchar(5), #BPPB nvarchar(5)'
, #BPPA = N'BPP2', #BPPB = N'BPP4'
Instead of the constants, you can also pass the values from your variables like this (replacing the last line of my sample script):
, #BPPA = #BPPA, #BPPB = #BPPB

I think that you can try this code below. I don't have for right now any access to SQL Server :)
DECLARE #BPPA AS nvarchar(5)
SET #BPPA = '''%,BPP2,%'''
DECLARE #BPPB AS nvarchar(5)
SET #BPPB = '''%,BPP4,%'''
DECLARE #sSQL AS varchar(750)
SET #sSQL = 'SELECT T0.CustCode AS ''CustCode'', MAX(T0.CustName) AS ''CustName'', T0.ItemCode AS ''ItemCode'', MAX(T0.ItemName) AS ''ItemName'', MAX(T0.BPP) AS ''BPP'', SUM(T0.Qty) AS ''QtyP1'', SUM(T0.SalesAmt) AS ''SalesAmtP1'', 0 AS ''QtyP2'', 0 AS ''SalesAmtP2'' FROM [dbo].[VWAJ_SALANALYSIS] T0 WHERE T0.DocDate BETWEEN ''2016-01-01'' AND ''2016-03-31'' AND ( (('',''' + RTRIM(T0.BPP) + ''','') LIKE N' + #BPPA + ') OR (('',''' + RTRIM(T0.BPP) + ''','') LIKE N' + #BPPB + ') ) GROUP BY T0.CustCode, T0.ItemCode ')
EXEC (#sSQL)
Hope this can help you

Related

Error converting data type varchar to numeric in standard audit trigger

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

Fields not showing in Crystal Reports

Hi I have the following dyanmic stored procedure which returns a normal result set at the end however crystal reports is not returning any fields along with it might anyone have an idea what is wrong.
When I run the proc in studio management it works fine. This is a report using the sap version of crystal in case that makes a difference.
I would normally output the results to excel and it runs correctly but for crystal its not allowing me to select any fields in the field explorer.
ALTER PROCEDURE [dbo].[ReportByDates]
#DateFrom date NULL,
#DateTo date NULL
AS
SET NOCOUNT ON;
DECLARE #DynamicColumnsDivision AS VARCHAR(max)
DECLARE #DynamicColumnsDivision2 AS VARCHAR(max)
DECLARE #DynamicColumnsDivisionTotal AS VARCHAR(max)
SELECT #DynamicColumnsDivision = COALESCE(#DynamicColumnsDivision + ', ', '')
+ 'ISNULL(' +Quotename(fieldname) + ',0) as ' + Quotename(fieldname)
FROM (
SELECT
DISTINCT
PrcCode + ' - ' + cc.PrcName as fieldname
FROM OPRC cc
WHERE Active = 'Y' AND CCTypeCode = 'Division'
) AS FieldList
SELECT #DynamicColumnsDivision2 = COALESCE(#DynamicColumnsDivision2 + ', ', '')
+ Quotename(fieldname)
FROM (
SELECT
DISTINCT
PrcCode + ' - ' + cc.PrcName as fieldname
FROM OPRC cc
WHERE Active = 'Y' AND CCTypeCode = 'Division'
) AS FieldList
SELECT #DynamicColumnsDivisionTotal = COALESCE(#DynamicColumnsDivisionTotal + '+', '')
+ 'ISNULL(' +Quotename(fieldname) + ',0)'
FROM (
SELECT
DISTINCT
PrcCode + ' - ' + cc.PrcName as fieldname
FROM OPRC cc
WHERE Active = 'Y' AND CCTypeCode = 'Division'
) AS FieldList
DECLARE #DynamicColumnsGQO AS VARCHAR(max)
DECLARE #DynamicColumnsGQO2 AS VARCHAR(max)
DECLARE #DynamicColumnsGQOTotal as VARCHAR(max)
SELECT #DynamicColumnsGQO = COALESCE(#DynamicColumnsGQO + ', ', '')
+ 'ISNULL(' +Quotename(fieldname) + ',0) as ' + Quotename(fieldname)
FROM (
SELECT
DISTINCT
PrcCode + ' - ' + cc.PrcName as fieldname
FROM OPRC cc
WHERE Active = 'Y' AND CCTypeCode = 'GQO'
) AS FieldList
SELECT #DynamicColumnsGQO2 = COALESCE(#DynamicColumnsGQO2 + ', ', '')
+ Quotename(fieldname)
FROM (
SELECT
DISTINCT
PrcCode + ' - ' + cc.PrcName as fieldname
FROM OPRC cc
WHERE Active = 'Y' AND CCTypeCode = 'GQO'
) AS FieldList
SELECT #DynamicColumnsGQOTotal = COALESCE(#DynamicColumnsGQOTotal + '+', '')
+ 'ISNULL(' +Quotename(fieldname) + ',0)'
FROM (
SELECT
DISTINCT
PrcCode + ' - ' + cc.PrcName as fieldname
FROM OPRC cc
WHERE Active = 'Y' AND CCTypeCode = 'GQO'
) AS FieldList
DECLARE #Query NVARCHAR(MAX)
SET #Query =
'SELECT Account, AcctName, ' + #DynamicColumnsDivision + ', ' + #DynamicColumnsDivisionTotal + ' as DivisionTotal , ' + #DynamicColumnsGQO + ', ' + #DynamicColumnsGQOTotal + ' as GQOToatl
FROM
(
SELECT
line.Account,
nom.AcctName,
line.ProfitCode + '' - '' + cc.PrcName as fieldname,
CASE WHEN line.Debit <> 0 THEN line.Debit *-1 WHEN line.Credit <> 0 THEN line.Credit ELSE 0 END as DispValue
FROM OJDT head
LEFT JOIN JDT1 line on head.TransId = line.TransId
LEFT JOIN OACT nom on line.Account = nom.AcctCode
LEFT JOIN OPRC cc ON line.ProfitCode = cc.PrcCode
WHERE head.RefDate BETWEEN ''' + CONVERT(VARCHAR(10),#DateFrom, 101) + ''' AND ''' + CONVERT(VARCHAR(10),#DateTo, 101) + '''
) AS SourceTable PIVOT
(
SUM(DispValue)
FOR fieldname IN (' + #DynamicColumnsDivision2 + ', '+#DynamicColumnsGQO2 + ')
) AS PivotTable;
'
--SELECT #Query
execute(#Query)
Edit 2
Weird I copied the entire proc and declared the date from and date to elements and it worked fine. So why when it being called from a stored proc is it working ok.
If the stored procedure does not return any data Crystal Reports won't be able to show you the columns. Make sure it returns the data.
Anyways, you can always try to remove the stored procedure and add it again in the database assistant. Crystal Reports is not very good updating the fields when you use a stored procedure, or at least it wasn't in older versions.

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;

Step through 2 temp tables compare to table

I have a SP that is supposed to compare 2 temp tables generated from a function to another table. My issue is that instead of stepping through each temp table it goes through both at the same time. I am not sure how to write the code to step through each table one at a time to get the desired results.
DROP PROCEDURE uspJudgments;
GO
CREATE PROCEDURE uspJudgments
#fullName varchar(100), #fullName1 varchar(100)
AS
BEGIN
SELECT *
INTO #tmpFullname
FROM dbo.DelimitedSplit8K(#fullName, ',')
SELECT *
INTO #tmpFullname1
FROM dbo.DelimitedSplit8K(#fullName1, ',')
SELECT *
FROM #tmpFullName
SELECT *
FROM #tmpFullName1
DECLARE #MaxRownum int
SET #MaxRownum = (SELECT MAX(ItemNumber) FROM #tmpFullname)
DECLARE #Iter int
SET #Iter = (SELECT MIN(ItemNumber) FROM #tmpFullname)
DECLARE #MaxRownum1 int
SET #MaxRownum1 = (SELECT MAX(ItemNumber) FROM #tmpFullname1)
DECLARE #Iter1 int
SET #Iter1 = (SELECT MIN(ItemNumber) FROM #tmpFullname1)
DECLARE #Name varchar(25)
DECLARE #Name1 varchar(25)
WHILE #Iter <= #MaxRownum AND #iter1 <= #Maxrownum1
BEGIN
SET #Name = (SELECT Item FROM #tmpFullname WHERE ItemNumber = #Iter)
SET #Name1 = (SELECT Item FROM #tmpFullname1 WHERE ItemNumber = #Iter1)
SELECT *
--INTO #tmpDefSelect
FROM defendants_ALL
WHERE combined_name LIKE '%' + #Name + '%' AND combined_name LIKE '%' + #Name1 + '%';
SET #Iter = #Iter + 1
SET #Iter1 = #Iter1 + 1
END
END
DROP TABLE #tmpFullname
DROP TABLE #tmpFullname1
EXEC uspJudgments #fullName = 'grein,smit', #fullName1 = 'joh,jon,j.'
I need to get ALL results for grein -- joh, jon, j. AND smit, joh, jon, j.
Currently the above code only returns grein joh AND smit, jon.. In our database that returns no results for the first combination and 38 results for jonathon smith and jon smith. How do I properly step through each temp table to get the desired results?
You only get the two combinations grein joh and smith jon because of the way you are iterating through #Iter and #iter1. You increment both of them at the same time. You need a nested loop like this:
declare #start_val integer = #iter1
while #Iter <= MaxRownum
begin
set #iter1 = #start_val
while #iter1 <= #Maxrownum1
begin
set #Name = ...
set #Name1 = ...
...
set #iter1 = #iter1 + 1
end
set #Iter = #Iter + 1
end
But I don't think you even need the WHILE loops:
select d.*
from defendants_ALL, #tmpFullname t1, #tmpFullname1 t2
where d.combined_name = t1.Item + ' ' + t2.Item
Or (if you need to still use LIKE):
select d.*
from defendants_ALL, #tmpFullname t1, #tmpFullname1 t2
where d.combined_name like '%' + t1.Item + '%'
and d.combined_name like '%' + t2.Item + '%'
(All SQL is untested...)

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'