How do you check if a certain index exists in a table? - sql-server

Something like this:
SELECT
*
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_NAME ='FK_TreeNodesBinaryAssets_BinaryAssets'
and TABLE_NAME = 'TreeNodesBinaryAssets'
but for indexes.

You can do it using a straight forward select like this:
SELECT *
FROM sys.indexes
WHERE name='YourIndexName' AND object_id = OBJECT_ID('Schema.YourTableName')

For SQL 2008 and newer, a more concise method, coding-wise, to detect index existence is by using the INDEXPROPERTY built-in function:
INDEXPROPERTY ( object_ID , index_or_statistics_name , property )
The simplest usage is with the IndexID property:
If IndexProperty(Object_Id('MyTable'), 'MyIndex', 'IndexID') Is Null
If the index exists, the above will return its ID; if it doesn't, it will return NULL.

AdaTheDEV, I used your syntax and created the following and why.
Problem: Process runs once a quarter taking an hour due to missing index.
Correction: Alter query process or Procedure to check for index and create it if missing... Same code is placed at the end of the query and procedure to remove index since it is not needed but quarterly. Showing Only drop syntax here
-- drop the index
begin
IF EXISTS (SELECT * FROM sys.indexes WHERE name='Index_Name'
AND object_id = OBJECT_ID('[SchmaName].[TableName]'))
begin
DROP INDEX [Index_Name] ON [SchmaName].[TableName];
end
end

If the hidden purpose of your question is to DROP the index before making INSERT to a large table, then this is useful one-liner:
DROP INDEX IF EXISTS [IndexName] ON [dbo].[TableName]
This syntax is available since SQL Server 2016. Documentation for IF EXISTS:
https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/
In case you deal with a primery key instead, then use this:
ALTER TABLE [TableName] DROP CONSTRAINT IF EXISTS [PK_name]

A slight deviation from the original question however may prove useful for future people landing here wanting to DROP and CREATE an index, i.e. in a deployment script.
You can bypass the exists check simply by adding the following to your create statement:
CREATE INDEX IX_IndexName
ON dbo.TableName
WITH (DROP_EXISTING = ON);
Read more here: CREATE INDEX (Transact-SQL) - DROP_EXISTING Clause
N.B. As mentioned in the comments, the index must already exist for this clause to work without throwing an error.

Wrote the below function that allows me to quickly check to see if an index exists; works just like OBJECT_ID.
CREATE FUNCTION INDEX_OBJECT_ID (
#tableName VARCHAR(128),
#indexName VARCHAR(128)
)
RETURNS INT
AS
BEGIN
DECLARE #objectId INT
SELECT #objectId = i.object_id
FROM sys.indexes i
WHERE i.object_id = OBJECT_ID(#tableName)
AND i.name = #indexName
RETURN #objectId
END
GO
EDIT: This just returns the OBJECT_ID of the table, but it will be NULL if the index doesn't exist. I suppose you could set this to return index_id, but that isn't super useful.

-- Delete index if exists
IF EXISTS(SELECT TOP 1 1 FROM sys.indexes indexes INNER JOIN sys.objects
objects ON indexes.object_id = objects.object_id WHERE indexes.name
='Your_Index_Name' AND objects.name = 'Your_Table_Name')
BEGIN
PRINT 'DROP INDEX [Your_Index_Name] ON [dbo].[Your_Table_Name]'
DROP INDEX [our_Index_Name] ON [dbo].[Your_Table_Name]
END
GO

EXEC sp_helpindex '[[[SCHEMA-NAME.TABLE-NAME]]]'
GO

To check Clustered Index exist on particular table or not:
SELECT * FROM SYS.indexes
WHERE index_id = 1 AND name IN (SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'Table_Name')

Related

T-SQL Check if table exists in schema

So far I've been using the format below for creating/updating tables
IF EXISTS (SELECT 1 FROM sysobjects WHERE name = 'table_name' AND type = 'U')
DROP TABLE [dbo].[table_name]
GO
CREATE TABLE [dbo].[table_name]()
GO
But recently I came across a case where two schemas have a table with the same name. How can I check if the table exists in a specific schema? Its only the partSELECT 1 FROM sysobjects WHERE name = 'table_name' AND type = 'U' that needs fixing, I've changed the rest to:
IF EXISTS (SELECT 1 FROM sysobjects WHERE name = 'table_name' AND type = 'U')
DROP TABLE [schema_name].[table_name]
GO
CREATE TABLE [schema_name].[table_name]()
GO
My current server version is 2008R2 so I would prefer answers that also work for that version. I have many other checks is done this way so I don't really want to completely change this pattern.
TRY
IF OBJECT_ID('[schema_name].[table_name]') IS NOT NULL
DROP TABLE [schema_name].[table_name]
GO
You could use the schemas object as well. For example:
IF EXISTS (SELECT 1
FROM sys.tables t
JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE s.[name] = N'schema_name'
AND t.name = N'table_name'
AND t.type = 'U')
DROP TABLE [schema_name].[table_name];
GO
Use this syntax.
DROP TABLE IF EXISTS [schema_name].[table_name]

'IF' does not prevent error, but only if it has already executed

I'm trying to move a column from one table to another (here's the post for that),
However this runs as a task, and may run after it has already completed so I need a clause that prevents the logic from running again. I thought I could achieve this with an IF:
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Table_A')
AND name = 'internalID'
)
BEGIN
UPDATE Table_B
SET b.internalID = a.internal_ID
FROM Table_B b INNER JOIN
Table_A a
ON a.id = b.FK_toTableA;
ALTER TABLE Table_A DROP COLUMN internalID;
END
However, I get an error at
SET b.internalID = a.internal_ID
The error is:
Invalid column name 'internalID'.
But Only If a.internalID doesn't exist anymore.
I had to use EXEC sp_executesql:
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Table_A')
AND name = 'internalID'
)
BEGIN
EXEC sp_executesql N'UPDATE Table_B
SET b.internalID = a.internal_ID
FROM Table_B b INNER JOIN
Table_A a
ON a.id = b.FK_toTableA';
ALTER TABLE Table_A DROP COLUMN internalID;
END
I guess because SQL Server complies the whole thing - even that stuff bypassed via conditional logic - before running the script.
Here's a good article about what sql_executesql does, compared to EXEC, but it basically boils down to more injection prevention.

Dynamic if exists

I want to check the condition if the index present in the table then fire stored procedure else other condition must be proceed.
Example:
IF EXISTS (SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID(#TableName)
BEGIN
execute spTest1
END
ELSE
BEGIN
execute spTest2
END
Note: In the above script the #TableName will be pass dynamically in the format of within single quote for example 'tableName'.
There's nothing wrong with the SQL, apart from a missing close bracket on the end of the EXISTS line:
IF EXISTS (SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID(#TableName))
BEGIN
execute spTest1
END
ELSE
BEGIN
execute spTest2
END
This will work, but you might always find an index without filtering the data in some way. You might want to be more specific about the type of indexes you are searching for as SQL will create some default indexes. For example if you create a table with a primary key, a clustered index will be created for you.
If no primary key is added you will usually have a HEAP type index, so this might be the type of index you are looking to filter.
If you run this against your target database, you will see the indexes that exist:
select st.name, st.object_id, si.name, si. index_id, si.type, si.type_desc
from sys.tables st
inner join sys.indexes si on si.object_id = st.object_id
In order to filter HEAP indexes, you could exclude clustered/non-clustered indexes you could filter by sys.indexes.index_id:
Heap: index_id = 0
Clustered: index_id = 1
Non Clustered: index_id = 2

Searchable text is in 2 tables, how to design full text index?

In a forum application, the actual name of the thread is stored in a table, and then replies is stored in another table.
Table_Thread
Subject varchar(255) e.g. "How to setup fulltext search"
Table_Replies (users replies here)
ReplyText text(not null)
Now I want to create a full-text search on both the subject and reply columns, but they seem very related so they should be in the same index.
Is it possible to do this?
I'm using sql server 2005.
Assuming there is an association between the subject and the replies you could create a view WITH SCHEMABINDING, create a UNIQUE CLUSTERED index on the view and then add that view to your fulltext catalog selecting the two columns you want included.
When huge concurrent query requests come, RDBMS cannot afford it by SQL. what's more, select SQL supports full-text search badly. So you need IR (Information Retrieval) library such as Lucene for java.
You could create a indexed view containing an union of both indexed columns + PK of the tables
e.g.
CREATE VIEW SearchText
WITH SCHEMABINDING
AS SELECT * FROM (
(Subject as Text, Table_Thread_ID as ID, 1 as Type FROM Table_Thread)
UNION ALL
(ReplyText as Text, Table_Replies_ID as ID, 2 as Type FROM Table_Replies));
I put type 1 and 2 as arbitrary, since you need a unique key to build a fulltext index.
And then create a unique index on (ID, Type), and finally your fulltext index.
CREATE UNIQUE INDEX SearchText_UK ON SearchText (ID, Type);
CREATE FULLTEXT CATALOG ft AS DEFAULT;
CREATE FULLTEXT INDEX ON SearchText(Text)
KEY INDEX SearchText_UK
WITH STOPLIST = SYSTEM;
I have seen what NopCommerce (C# MVC Open Source E-Commerce) has done using fulltext search on 'products' and 'variants' and only return 'products'. This is very similar to your case because you want to search on 'Thread' and 'Replies' but you obviously want to only return 'threads'. I have change it to use threads and replies for you:
First, create a function that generates an index name by table (optional):
CREATE FUNCTION [dbo].[nop_getprimarykey_indexname]
(
#table_name nvarchar(1000) = null
)
RETURNS nvarchar(1000)
AS
BEGIN
DECLARE #index_name nvarchar(1000)
SELECT #index_name = i.name
FROM sys.tables AS tbl
INNER JOIN sys.indexes AS i ON (i.index_id > 0 and i.is_hypothetical = 0) AND (i.object_id=tbl.object_id)
WHERE (i.is_unique=1 and i.is_disabled=0) and (tbl.name=#table_name)
RETURN #index_name
END
GO
Then, enable fulltext by creating the catalog and the indexes:
EXEC('
IF NOT EXISTS (SELECT 1 FROM sys.fulltext_catalogs WHERE [name] = ''myFullTextCatalog'')
CREATE FULLTEXT CATALOG [myFullTextCatalog] AS DEFAULT')
DECLARE #create_index_text nvarchar(4000)
SET #create_index_text = '
IF NOT EXISTS (SELECT 1 FROM sys.fulltext_indexes WHERE object_id = object_id(''[Table_Thread]''))
CREATE FULLTEXT INDEX ON [Table_Thread]([Subject])
KEY INDEX [' + dbo.[nop_getprimarykey_indexname] ('Table_Thread') + '] ON [myFullTextCatalog] WITH CHANGE_TRACKING AUTO'
EXEC(#create_index_text)
SET #create_index_text = '
IF NOT EXISTS (SELECT 1 FROM sys.fulltext_indexes WHERE object_id = object_id(''[Table_Replies]''))
CREATE FULLTEXT INDEX ON [Table_Replies]([ReplyText])
KEY INDEX [' + dbo.[nop_getprimarykey_indexname] ('Table_Replies') + '] ON [myFullTextCatalog] WITH CHANGE_TRACKING AUTO'
EXEC(#create_index_text)
Then, in the stored procedure to obtain products by keywords, build a temporary table with a list of product Ids that match the keywords.
INSERT INTO #KeywordThreads ([ThreadId])
SELECT t.Id
FROM Table_Thread t with (NOLOCK)
WHERE CONTAINS(t.[Subject], #Keywords)
UNION
SELECT r.ThreadId
FROM Table_Replies r with (NOLOCK)
WHERE CONTAINS(pv.[ReplyText], #Keywords)
Now you can use the temporary table #KeywordThreads to join with the list of threads and return them.
I hope this helps.

Add a column to a table, if it does not already exist

I want to write a query for MS SQL Server that adds a column into a table. But I don't want any error display, when I run/execute the following query.
I am using this sort of query to add a table ...
IF EXISTS (
SELECT *
FROM sys.objects
WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[Person]')
AND TYPE IN (N'U')
)
But I don't know how to write this query for a column.
You can use a similar construct by using the sys.columns table io sys.objects.
IF NOT EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[dbo].[Person]')
AND name = 'ColumnName'
)
IF COL_LENGTH('table_name', 'column_name') IS NULL
BEGIN
ALTER TABLE table_name
ADD [column_name] INT
END
Another alternative. I prefer this approach because it is less writing but the two accomplish the same thing.
IF COLUMNPROPERTY(OBJECT_ID('dbo.Person'), 'ColumnName', 'ColumnId') IS NULL
BEGIN
ALTER TABLE Person
ADD ColumnName VARCHAR(MAX) NOT NULL
END
I also noticed yours is looking for where table does exist that is obviously just this
if COLUMNPROPERTY( OBJECT_ID('dbo.Person'),'ColumnName','ColumnId') is not null
Here's another variation that worked for me.
IF NOT EXISTS (SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE upper(TABLE_NAME) = 'TABLENAME'
AND upper(COLUMN_NAME) = 'COLUMNNAME')
BEGIN
ALTER TABLE [dbo].[Person] ADD Column
END
GO
EDIT:
Note that INFORMATION_SCHEMA views may not always be updated, use SYS.COLUMNS instead:
IF NOT EXISTS (SELECT 1
FROM SYS.COLUMNS....
IF NOT EXISTS (SELECT * FROM syscolumns
WHERE ID=OBJECT_ID('[db].[Employee]') AND NAME='EmpName')
ALTER TABLE [db].[Employee]
ADD [EmpName] VARCHAR(10)
GO
I Hope this would help. More info
When checking for a column in another database, you can simply include the database name:
IF NOT EXISTS (
SELECT *
FROM DatabaseName.sys.columns
WHERE object_id = OBJECT_ID(N'[DatabaseName].[dbo].[TableName]')
AND name = 'ColumnName'
)
IF NOT EXISTS (SELECT 1 FROM SYS.COLUMNS WHERE
OBJECT_ID = OBJECT_ID(N'[dbo].[Person]') AND name = 'DateOfBirth')
BEGIN
ALTER TABLE [dbo].[Person] ADD DateOfBirth DATETIME
END

Resources