IF NOT EXISTS(SELECT * FROM sys.indexes WHERE name = 'MyTableIndex' AND object_id = OBJECT_ID('tablename'))
BEGIN
-- Index with this name, on this table does NOT exist
END
Answer from AdaTheDev on Stack OverflowWhen I drop indexes, I usually use a “drop index if exists” just in case in instances it’s already been dropped that it won’t error. Is there a way to do something similar on the Create Index?
How do I check if a nonclustered index exists in SQL Server 2005 - Stack Overflow
azure sql database - SQL Server: "if not exists... create index" pattern fails "because an index or statistics already exists" - Stack Overflow
sql server - Create an index without redundant code - Database Administrators Stack Exchange
sql - Creating index if index doesn't exist - Stack Overflow
IF NOT EXISTS(SELECT * FROM sys.indexes WHERE name = 'MyTableIndex' AND object_id = OBJECT_ID('tablename'))
BEGIN
-- Index with this name, on this table does NOT exist
END
Try this:
IF NOT EXISTS(SELECT * FROM sys.indexes WHERE Name = 'MyTableIndex')
-- put your CREATE INDEX statement here
In SQL Server 2016, this is very easy, you just need to make a choice between having simple scripts or enjoying whatever performance you've actually observed from DROP_EXISTING (is this quantifiable? Have you tested it?).
CREATE TABLE dbo.what(i int, INDEX x(i));
GO
DROP INDEX IF EXISTS dbo.what.x;
GO
CREATE INDEX x ON dbo.what(i DESC);
GO
So, your requirements are:
- Create all indexes on the list, dropping and replacing existing indexes.
- If the index does exist, use
WITH (DROP_EXISTING = ON). - No repetition of the code to create the index.
I only see two options that would do this:
Option 1: Dynamic SQL
Build the basic CREATE INDEX command; then, if the index exists, tack on the DROP_EXISTING clause.
DECLARE @stmt NVARCHAR(MAX);
SET @stmt = N'
CREATE NONCLUSTERED INDEX [IX_a]
ON [dbo].animals
INCLUDE([ID], [Currency])'
+ CASE WHEN EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[animals]') AND name = N'IX_a')
THEN N' WITH (DROP_EXISTING = ON)'
ELSE N''
END
+ N';'
;
EXECUTE sp_executesql @stmt;
Obviously, this solution has some issues of its own. In general, you have to maintain the statements as strings. The biggest issues here are that you have to remember to double any single quotes that should be in the statement. As the sample statement has none, this may not turn out to be an issue, but it must be noted. In particular, the statement must be altered if need other WITH options set. Again, in this example, that's not an issue. If all statements did have existing WITH options to include, it would be a huge issue either (though the statement would need tweaked:
SET @stmt = N'
CREATE NONCLUSTERED INDEX [IX_a]
ON [dbo].animals
INCLUDE([ID], [Currency]) WITH (DATA_COMPRESSION = PAGE'
+ CASE WHEN EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[animals]') AND name = N'IX_a')
THEN N', DROP_EXISTING = ON'
ELSE N''
END
+ N');'
;
Option 2: create a "dummy" index
Note that I wouldn't recommend this; it's actually at least as hard to maintain as simply having two copies of the CREATE INDEX statement, and causes extra work on the system. However, if you would expect that the index would almost always be there, it might be useful.
First, you create an index with the name you want (the columns don't matter); then, replace it with the index you actually want.
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[animals]') AND name = N'IX_a')
CREATE NONCLUSTERED INDEX [IX_a]
ON [dbo].[animals] ([BioNr] ASC);
CREATE NONCLUSTERED INDEX [IX_a]
ON [dbo].animals
INCLUDE([ID], [Currency]) WITH (DROP_EXISTING = ON);
As I said, I wouldn't recommend it, but it would (technically, at least) meet your requirements, so I thought I should mention it.
Final Note
It's probably worth posting a CONNECT item, requesting that DROP_EXISTING = ON be allowed whether the index exists or not (if you can't find an existing one, at least). It seems entirely reasonable for the option to simply be ignored if the index doesn't exist.
Take a look at the System tables:
https://devzone.advantagedatabase.com/dz/webhelp/Advantage10/devguide_system_tables.htm
In particular there is a table called system.indexes:
https://devzone.advantagedatabase.com/dz/webhelp/Advantage10/master_system_indexes.htm
Try something more like this, utilizing the system commands. This is a working example I use on an Advantage Database:
IF (SELECT Name FROM system.indexes
WHERE Index_File_Name = 'GLDept.adi'
AND Index_Expression = 'DeptNumber') IS NULL
THEN
EXECUTE PROCEDURE sp_CreateIndex90(
'GLDept',
'GLDept.adi',
'DEPTNUMBER',
'DeptNumber',
'',
2051,
512,
'' );
END IF;
Index names in PostgreSQL
- Index names are unique across a single database schema.
- Index names cannot be the same as any other index, (foreign) table, (materialized) view, sequence or user-defined composite type in the same schema.
- Two tables in the same schema cannot have an index of the same name. (Follows logically.)
If you do not care about the name of the index, have Postgres auto-name it:
CREATE INDEX ON tbl1 (col1);
is (almost) the same as:
CREATE INDEX tbl1_col1_idx ON tbl1 USING btree (col1);
Except that Postgres will avoid a naming collisions and automatically pick the next free name:
tbl1_col1_idx
tbl1_col1_idx2
tbl1_col1_idx3
...
Just try it. But, obviously, you would not want to create multiple redundant indexes. So it wouldn't be a good idea to just blindly create a new one.
Test for existence
Postgres 9.5 or newer
Now available:
CREATE INDEX IF NOT EXISTS ...
Also works for CREATE INDEX CONCURRENTLY IF NOT EXISTS.
However, the manual warns:
Note that there is no guarantee that the existing index is anything like the one that would have been created.
It's a plain check for the object name. (Applies to variants for older versions below, too.)
To find existing indexes on the same table for the same column(s):
SELECT pg_get_indexdef(indexrelid)
FROM pg_index
WHERE indrelid = 'public.big'::regclass
AND (indkey::int2[])[:] = ARRAY (
SELECT attnum
FROM unnest('{usr_id, created_at}'::text[]) WITH ORDINALITY i(attname, ord)
JOIN (
SELECT attname, attnum
FROM pg_attribute
WHERE attrelid = 'public.big'::regclass
) a USING (attname)
ORDER BY ord
);
Restrictions:
- Only works for columns, not other index expressions.
- Also reports partial indexes (with
WHEREclause) and covering indexes (withINCLUDEclause). - Reports any type of index, not just B-tree indexes.
Study the results (if any) before proceeding, or refine the query to your needs ...
Further reading:
- Find tables with multiple indexes on same column
- Normalize array subscripts for 1-dimensional array so they start with 1
Postgres 9.4
You can use the new function to_regclass() to check without throwing an exception:
DO
$$
BEGIN
IF to_regclass('myschema.mytable_mycolumn_idx') IS NULL THEN
CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
END IF;
END
$$;
Returns NULL if an index (or another object) of that name does not exist. See:
- How to check if a table exists in a given schema
This doesn't work for CREATE INDEX CONCURRENTLY, since that variant cannot be wrapped in an outer transaction. See comment by @Gregory below.
Postgres 9.3 or older
Cast the schema-qualified name to regclass:
SELECT 'myschema.myname'::regclass;
If it throws an exception, the name is free.
Or, to test the same without throwing an exception, use a DO statement:
DO
$$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable_mycolumn_idx'
AND n.nspname = 'myschema'
) THEN
CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
END IF;
END
$$;
The DO statement was introduced with Postgres 9.0. In earlier versions you have to create a function to do the same.
Details about pg_class in the manual.
Basics about indexes in the manual.
It will be available in 9.5. Here is actual git commit https://github.com/postgres/postgres/commit/08309aaf74ee879699165ec8a2d53e56f2d2e947
Discussion on pg hackers http://postgresql.nabble.com/CREATE-IF-NOT-EXISTS-INDEX-td5821173.html