How do I query if a database schema exists - sql-server-2005

As part of our build process we run a database update script as we deploy code to 4 different environments. Further, since the same query will get added to until we drop a release into production it has to be able to run multiple times on a given database. Like this:
IF NOT EXISTS (SELECT * FROM sys.tables WHERE object_id = OBJECT_ID(N'[Table]'))
BEGIN
CREATE TABLE [Table]
(...)
END
Currently I have a create schema statement in the deployment/build script. Where do I query for the existence of a schema?

Are you looking for sys.schemas?
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'jim')
BEGIN
EXEC('CREATE SCHEMA jim')
END
Note that the CREATE SCHEMA must be run in its own batch (per the answer below)

#bdukes is right on the money for determining if the schema exists, but the statement above won't work in SQL Server 2005. CREATE SCHEMA <name> needs to run in its own batch. A work around is to execute the CREATE SCHEMA statement in an exec.
Here is what I used in my build scripts:
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = '<name>')
BEGIN
-- The schema must be run in its own batch!
EXEC( 'CREATE SCHEMA <name>' );
END

This is old so I feel compelled to add: For SQL SERVER 2008+ These all work (for the select part), then use EXECUTE('CREATE SCHEMA <name>') to actually create it on negative results.
DECLARE #schemaName sysname = 'myfunschema';
-- shortest
If EXISTS (SELECT 1 WHERE SCHEMA_ID(#schemaName) IS NOT NULL)
PRINT 'YEA'
ELSE
PRINT 'NOPE'
SELECT DB_NAME() AS dbname WHERE SCHEMA_ID(#schemaName) IS NOT NULL -- nothing returned if not there
IF NOT EXISTS ( SELECT top 1 *
FROM sys.schemas
WHERE name = #schemaName )
PRINT 'WOOPS MISSING'
ELSE
PRINT 'Has Schema'
SELECT SCHEMA_NAME(SCHEMA_ID(#schemaName)) AS SchemaName1 -- null if not there otherwise schema name returned
SELECT SCHEMA_ID(#schemaName) AS SchemaID1-- null if not there otherwise schema id returned
IF EXISTS (
SELECT sd.SchemaExists
FROM (
SELECT
CASE
WHEN SCHEMA_ID(#schemaName) IS NULL THEN 0
WHEN SCHEMA_ID(#schemaName) IS NOT NULL THEN 1
ELSE 0
END AS SchemaExists
) AS sd
WHERE sd.SchemaExists = 1
)
BEGIN
SELECT 'Got it';
END
ELSE
BEGIN
SELECT 'Schema Missing';
END

If the layout of components allows it, this works too.
IF EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'myschema') SET NOEXEC ON
go
CREATE SCHEMA myschema
GO
SET NOEXEC OFF -- if any further processing is needed.
GO

Just to be extra "defensive", the following version generates a Type conversion error to account for the possibility (however unlikely) of > 1 matching Schema's similar to how validation code often intentionally Throw Exception's because I believe it's good to and I believe it's "'best practice'" to account for all possible return results however unlikely and even if it's just to generate a fatal exception because the known effects of stopping processing is usually better than unknown cascading effects of un-trapped errors. Because it's highly unlikely, I didn't think it's worth the trouble of a separate Count check + Throw or Try-Catch-Throw to generate a more user-friendly fatal error but still fatal error nonetheless.
SS 2005-:
declare #HasSchemaX bit
set #HasSchemaX = case (select count(1) from sys.schemas where lower(name) = lower('SchemaX')) when 1 then 1 when 0 then 0 else 'ERROR' end
SS 2008+:
declare #HasSchemaX bit = case (select count(1) from sys.schemas where lower(name) = lower('SchemaX')) when 1 then 1 when 0 then 0 else 'ERROR' end
Then:
if #HasSchemaX = 1
begin
...
end -- if #HasSchemaX = 1

IF NOT EXISTS (SELECT TOP (1) 1 FROM [sys].[schemas] WHERE [name] = 'Person')
BEGIN
EXEC ('CREATE SCHEMA [Person]')
END
IF NOT EXISTS (SELECT TOP (1) 1 FROM [sys].[tables] AS T
INNER JOIN [sys].[schemas] AS S ON S.schema_id = T.schema_id
WHERE T.[name] = 'Guests' AND S.[name] = 'Person')
BEGIN
EXEC ('CREATE TABLE [Person].[Guests]
(
[GuestId] INT IDENTITY(1, 1) NOT NULL,
[Forename] NVARCHAR(100) NOT NULL,
[Surname] NVARCHAR(100) NOT NULL,
[Email] VARCHAR(255) NOT NULL,
[BirthDate] DATETIME2 NULL,
CONSTRAINT [PK_Guests_GuestId] PRIMARY KEY CLUSTERED ([GuestId]),
CONSTRAINT [UX_Guests_Email] UNIQUE([Email])
)')
END
NOTICE: CREATE SCHEMA AND CREATE TABLE NEED COMPLETLY SEPARATED BATCH TO EXECUTE
TO MORE DESCRIPTION VISIT MICROSOFT DOCS WEBSITE :)

As of SQL Server 2005 version 9.0 you can use the INFORMATION_SCHEMA.SCHEMATA view to check if the schema exists:
IF NOT EXISTS (
SELECT SCHEMA_NAME
FROM INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME = '<schema name>' )
BEGIN
EXEC sp_executesql N'CREATE SCHEMA <schema name>'
END
GO
INFORMATION_SCHEMA views are the ISO standard and are generally preferable; these were adopted to make the syntax more consistent across different SQL database platforms.
Note that the CREATE SCHEMA must be run in its own batch

Related

Stored Procedure Addition - Check if DB exists before proceeding

I'm current writing a stored procedure which does several things but I want to implement a change at the start which will cause the USP to proceed no further if the variable result which provides a Database Name does not exist.
Below is my current code which is used.
Basically, if the #DBName does not exist on the server, I want the stored procedure to proceed no further, but if it does exist, then I want to proceed.
DECLARE #FullyQualifiedTableName VARCHAR(100) = '[Sandbox].[Test].[USP_Test]';
DECLARE #DBName NVARCHAR(50) =
(
SELECT LEFT(#FullyQualifiedTableName, CHARINDEX('.', #FullyQualifiedTableName) - 1)
);
Thanks!
One way is to query the dictionary.
...
IF NOT EXISTS (SELECT *
FROM sys.databases
WHERE name = #dbname)
BEGIN
-- whatever you want to do if the database doesn't exist
END;
...
DECLARE #FullyQualifiedTableName VARCHAR(100) = '[Sandbox].[Test].[USP_Test]';
DECLARE #DBName NVARCHAR(50) = (SELECT LEFT(#FullyQualifiedTableName, CHARINDEX('.', #FullyQualifiedTableName) - 1));
IF EXISTS (SELECT TOP 1 1 FROM #DBName)
BEGIN
-- Your logic if DB Name Exists
END;
IF NOT EXISTS (SELECT TOP 1 1 FROM #DBName)
BEGIN
-- Your logic if DB Name Not Exists
END;
SELECT TOP 1 1 FROM [SomeTable] Here this query will return any 1 matching record it will satisfy the criteria.
IF EXISTS and IF NOT EXISTS are mainly used to check conditions. It will always return true or false based on your query.

How to Get Row Count of Table That May or May Not Exist?

I have an issue where I have two possible tables that may contain the data I need. I have accounted for this using a temp table, and an if-else if that fills the temp table from the appropriate table:
IF EXISTS (SELECT * FROM sys.tables WHERE [name] = "table1") BEGIN
--Fill temp table.
END ELSE BEGIN
IF EXISTS (SELECT * FROM sys.tables WHERE [name] = "table2") BEGIN
-- Fill temp table.
END
END
This works great as long as table1 no longer exists. However, I have come to learn that the old table still exists and contains no data thus causing an issue with my query not returning the proper results.
I have achieved a workaround; however, this will only work if the table exists, if the table does not exist (there are cases of this) then the query will not execute:
IF EXISTS(SELECT * FROM sys.tables WHERE [name] = "table1") AND
(SELECT COUNT(*) FROM table1) > 0 BEGIN
-- Fill temp table.
END
How do I get the row count of a table that may or may not exist?
Other Useful Notes: I am using SSMS 2016.
Good Suggestions: A fellow user stated that I should give EXEC a try; I am more than willing to go this route if need be. Our IA guy doesn't really like this approach and there is additional validation that would have to be done in order to make him happy but the quick and dirty version would be:
DECLARE #CMD NVARCHAR(MAX) = N'IF EXISTS(SELECT * FROM sys.tables WHERE [name] = "table1") AND
(SELECT COUNT(*) FROM table1) > 0 BEGIN
-- Fill temp table.
END'
EXEC sp_executesql #CMD;
Another good attempt would be to verify the existence of the new table first and prioritize it instead, however this does not answer the question behind this post; but for those with a similar situation this would work as well:
IF EXISTS (SELECT * FROM sys.tables WHERE [name] = "table2") BEGIN
--Fill temp table.
END ELSE BEGIN
IF EXISTS (SELECT * FROM sys.tables WHERE [name] = "table1") BEGIN
-- Fill temp table.
END
END
It turns out you can query the row count from sys.tables too:
IF EXISTS(SELECT * FROM sys.tables WHERE [name] = 'table1'
AND OBJECTPROPERTYEX([object_id], 'Cardinality') > 0)
This and a few alternative approaches are here on the SQLAuthority blog.
If you want fix your workaround just split the IF
IF EXISTS(SELECT * FROM sys.tables WHERE [name] = 'table1')
BEGIN
IF (SELECT COUNT(*) FROM table1) > 0
BEGIN
-- Fill temp table.
END
END

How to check if a view exists and create if it does not

I want to create a view if not exists in SQL Server 2016
IF EXISTS(SELECT 1 FROM sys.views
WHERE Name = 'VI_ALL_CITIES_AS_CATEGORY')
BEGIN
CREATE VIEW VI_ALL_CITIES_AS_CATEGORY AS
SELECT PERSONS.FIRST_NAME AS 'Име', PERSONS.LAST_NAME AS 'Фамилия', CITIES.CITY_NAME AS 'Град'
FROM CITIES
LEFT JOIN PERSONS ON CITIES.ID = PERSONS.CITY_ID ;
END
But it gives me error:
Incorrect syntax: 'CREATE VIEW' must be the only statement in the
batch.
You have 2 options:
1) If you are using SSMS or any client that can split your script into different batches:
IF EXISTS(SELECT 'view exists' FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = N'YourViewName'AND TABLE_SCHEMA = 'YourViewSchema')
BEGIN
DROP VIEW YourViewSchema.YourViewName
END
GO -- This will make the next statement the first in it's batch
CREATE VIEW YourViewSchema.YourViewName AS
SELECT something = 1
FROM YourTable
GO
2) If you can't split code into batches, you will have to "fool" the engine using dynamic SQL to create your view:
IF NOT EXISTS(SELECT 'view exists' FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = N'YourViewName'AND TABLE_SCHEMA = 'YourViewSchema')
BEGIN
DECLARE #v_ViewCreateStatement VARCHAR(MAX) = '
CREATE VIEW YourViewSchema.YourViewName AS
SELECT something = 1
FROM YourTable'
EXEC (#v_ViewCreateStatement)
END
Note that 1) is an IF EXISTS and 2) is an IF NOT EXISTS.
The reason for this is that most DDL statements need to be first in a batch, so you can't put CREATE object statements after other statements, unfortunately.
Try this approach
IF NOT EXISTS
(
SELECT 1
FROM sys.views
WHERE Name = 'VI_ALL_CITIES_AS_CATEGORY'
)
BEGIN
EXEC('CREATE VIEW VI_ALL_CITIES_AS_CATEGORY AS SELECT 1 as Val')
END
GO
ALTER VIEW VI_ALL_CITIES_AS_CATEGORY
AS
SELECT
PERSONS.FIRST_NAME AS 'Име',
PERSONS.LAST_NAME AS 'Фамилия',
CITIES.CITY_NAME AS 'Град'
FROM CITIES
LEFT JOIN PERSONS
ON CITIES.ID = PERSONS.CITY_ID
If you don't wanna to delete your current view, you can create a new one with a temp name, then use IF to rename, using exec sp_rename that is executed in only one batch, something like this:
CREATE VIEW VI_ALL_CITIES_AS_CATEGORY_TEMP AS
SELECT PERSONS.FIRST_NAME AS 'Име', PERSONS.LAST_NAME AS 'Фамилия', CITIES.CITY_NAME AS 'Град'
FROM CITIES
LEFT JOIN PERSONS ON CITIES.ID = PERSONS.CITY_ID ;
GO
IF OBJECT_ID('dbo.VI_ALL_CITIES_AS_CATEGORY', 'V') IS NULL
exec sp_rename 'dbo.VI_ALL_CITIES_AS_CATEGORY_TEMP', 'VI_ALL_CITIES_AS_CATEGORY'
GO
IF OBJECT_ID('dbo.VI_ALL_CITIES_AS_CATEGORY_TEMP', 'V') IS NOT NULL
drop view VI_ALL_CITIES_AS_CATEGORY_TEMP

How can I check if the table behind a synonym exists

I'm trying to create a simple script to dump the results of a complex view out into a table for reporting. I have used synonyms to simplify tweaking the view and table names.
The idea is that the user of the script can put the name of the view they want to use as the source, and the name of the target reporting table in at the start and away they go. If the table doesn't exist then the script should create it. If the table already exists then the script should only copy the records from the view which aren't already in the table over.
The script below covers all those requirements, but I can't find a nice way to check if the table behind the synonym already exists:
CREATE SYNONYM SourceView FOR my_view
CREATE SYNONYM TargetReportingTable FOR my_table
-- Here's where I'm having trouble, how do I check if the underlying table exists?
IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_name = TargetReportingTable) = 0
BEGIN
-- Table does not exists, so insert into.
SELECT * INTO TargetReportingTable FROM SourceView
END
ELSE
BEGIN
-- Table already exists so work out the last record which was copied over
-- and insert only the newer records.
DECLARE #LastReportedRecordId INT;
SET #LastReportedRecordId = (SELECT MAX(RecordId) FROM TargetReportingTable)
INSERT INTO TargetReportingTable SELECT * FROM SourceView WHERE RecordId > #LastReportedRecordId
END
DROP SYNONYM SourceView
DROP SYNONYM TargetReportingTable
I know I could just get the user of the script to copy the table name into the 'information_schema' line as well as into the synonym at the top, but that leaves scope for error.
I also know I could do something filthy like put the table name into a variable and blat the SQL out as a string, but that makes me feel a bit sick!
Is there a nice elegant SQL way for me to check if the table behind the synonym exists? Or a totally different way to solve to problem?
Not the most elegant of solutions, but you could join the sys.synonyms table to the sys.tables table to check whether the table exists.
If the table does not exist, the join will fail and you will get 0 rows (hence IF EXISTS will be false). If the table does exist, the join will success and you will get 1 row (and true):
IF EXISTS( SELECT *
FROM sys.synonyms s
INNER JOIN sys.tables t ON REPLACE(REPLACE(s.base_object_name, '[', ''), ']', '') = t.name
WHERE s.name = 'TargetReportingTable')
BEGIN
-- Does exist
END
ELSE
BEGIN
-- Does not exist
END
Replace 'TargetReportingTable' with whichever synonym you wish to check.
The above solutions did not work for me if the synonym referenced another database. I recently discovered the function [fn_my_permissions] which is useful for showing permissions for a specific database object, so I figure this could be used as follows:
IF EXISTS
(
select *
from sys.synonyms sy
cross apply fn_my_permissions(sy.base_object_name, 'OBJECT')
WHERE sy.name = 'TargetReportingTable'
)
print 'yes - I exist!'
Late to the party, I have created a query to test out the existence of Synonyms and share with you.
DECLARE #Synonyms table
(
ID int identity(1,1),
SynonymsDatabaseName sysname,
SynonymsSchemaName sysname,
SynonymsName sysname,
DatabaseName nvarchar(128),
SchemaName nvarchar(128),
ObjectName nvarchar(128),
Remark nvarchar(max),
IsExists bit default(0)
)
INSERT #Synonyms (SynonymsDatabaseName, SynonymsSchemaName, SynonymsName, DatabaseName, SchemaName, ObjectName)
SELECT
DB_NAME() AS SynonymsDatabaseName,
SCHEMA_NAME(schema_id) AS SynonymsSchemaName,
name AS SynonymsName,
PARSENAME(base_object_name,3) AS DatabaseName,
PARSENAME(base_object_name,2) AS SchemaName,
PARSENAME(base_object_name,1) AS ObjectName
FROM sys.synonyms
SET NOCOUNT ON
DECLARE #ID int = 1, #Query nvarchar(max), #Remark nvarchar(max)
WHILE EXISTS(SELECT * FROM #Synonyms WHERE ID = #ID)
BEGIN
SELECT
#Query = 'SELECT #Remark = o.type_desc FROM [' + DatabaseName + '].sys.objects o INNER JOIN sys.schemas s ON o.schema_id = s.schema_id WHERE s.name = ''' + SchemaName + ''' AND o.name = ''' + ObjectName + ''''
FROM #Synonyms WHERE ID = #ID
EXEC sp_executesql #Query, N'#Remark nvarchar(max) OUTPUT', #Remark OUTPUT;
UPDATE #Synonyms SET IsExists = CASE WHEN #Remark IS NULL THEN 0 ELSE 1 END, Remark = #Remark WHERE ID = #ID
SELECT #ID += 1, #Remark = NULL
END
SELECT * FROM #Synonyms
You can do this with dynamic SQL:
-- create synonym a for information_schema.tables
create synonym a for b
declare #exists int = 1;
begin try
exec('select top 0 * from a');
end try
begin catch
set #exists = 0;
end catch
select #exists;
This doesn't work with non-dynamic SQL, because the synonym reference is caught at compile-time. That means that the code just fails with a message and is not caught by the try/catch block. With dynamic SQL, the block catches the error.
You can test if Synonym exists in your database using the Object_Id function avaliable in SQL Server
IF OBJECT_ID('YourDatabaseName..YourSynonymName') IS NOT NULL
PRINT 'Exist SYNONYM'
ELSE
PRINT 'Not Exist SYNONYM'
Another simpler solution:
IF (EXISTS (SELECT * FROM sys.synonyms WHERE NAME ='mySynonymName'))
BEGIN
UPDATE mySynonymName
SET [Win] = 1
END
In this case, I do database setup first. I drop all Synonyms in my database (database1) first, then run a SPROC to create synonyms for all tables in the destination database(database2).
Some SPROCS in database1 call on tables in DB2. If table doesnt exist in DB2 the SPROC fails. If table doesnt exist in DB2, the synonmy is not automatically created on database setup. So I just use the above to check if the Synonym exist, and skip that part of the SPROC if the Synonym is not present.

How to identify whether the table has identity column

I want to find out whether the table has an identity column or not. Table is unknown to me. I have not done the structure of the table. Using Query?
I am using Sql Server Compact Edition.
IF (OBJECTPROPERTY(OBJECT_ID('TABLE_NAME'), 'TableHasIdentity') = 1)
ObjectProperty is available starting sql server 2008 Reference:
OBJECTPROPERTY
This query returns a table's identity column name:
CREATE PROCEDURE dbo.usp_GetIdentity
#schemaname nvarchar(128) = 'dbo'
,#tablename nvarchar(128)
AS
BEGIN
SELECT OBJECT_NAME(OBJECT_ID) AS TABLENAME,
NAME AS COLUMNNAME,
SEED_VALUE,
INCREMENT_VALUE,
LAST_VALUE,
IS_NOT_FOR_REPLICATION
FROM SYS.IDENTITY_COLUMNS
WHERE OBJECT_NAME(OBJECT_ID) = #tablename
AND OBJECT_SCHEMA_NAME(object_id) = #schemaname
END
Then form the code side.
Call this stored procedure using the datareader role, then check datareader.hasrows(). If the condition value is true (1), then the table has identity column if set. If not then it doesn't have an identity column.
I know it's long time ago but i found this helpful
try this :
IF EXISTS (SELECT * from syscolumns where id = Object_ID(#TABLE_NAME) and colstat & 1 = 1)
BEGIN
-- Do your things
END
Any of the below queries can be used to check if an Identity Column is present in the table
1)
SELECT *
FROM sys.identity_columns
WHERE OBJECT_NAME(object_id) = 'TableName'
2)
SELECT *
FROM sys.identity_columns
WHERE object_id = (
SELECT id
FROM sysobjects
WHERE name = 'TableName'
)
I would just like to add this option as well as I think it is the simplest
SELECT COLUMNPROPERTY(OBJECT_ID('TableName'),'ColumnName','isidentity')
One way to do this would be to make use of the stored procedure sp_help. I.e:
sp_help MyTable
This will return a DataSet that has all the information you would need on the table. There is a specific Table that has information on identities.
I.e:
If it does not contain an identity field, the Identity column will say: "No identity column defined".
#Pranay: he said Compact Edition. Stored procedures aren't supported, and there is no sys.anything.
This is the call:
SELECT Count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE AUTOINC_INCREMENT IS NOT NULL AND TABLE_NAME='this_table'
It will return either 1 (true) or 0 (false).
...
declare #tblhasIdentCol bit = IF (IDENT_CURRENT( #dbName +'.'+ #schemaName +'.'+ #tableName ) IS NOT NULL , 1 , 0 )
You get numeric value if table has identity
Very simple answer would be to run this:
SELECT IDENT_CURRENT('TABLE-NAME')
This would give max value of identity column if exists, if the column doesn't exist, it gives 1 as result.
Based on max value, you can identify which column is having that and determine the identity column.
This the query that get u all the tableNames, columnnames of the table, and is_identity or not in the selected database
SELECT
sys.columns.name
, sys.tables.name
, is_identity
FROM sys.columns
INNER JOIN sys.tables ON sys.tables.object_id = sys.columns.object_id
AND sys.columns.is_identity = 1
CREATE FUNCTION dbo.fnTableHasIdentity(#Tbl sysname)
RETURNS TINYINT
BEGIN
RETURN OBJECTPROPERTY(OBJECT_ID(#Tbl), 'TableHasIdentity')
END
--As simple as that!
select t.name as TableName,c.name as ColumnName
from sys.identity_columns c
inner join sys.tables t on c.object_id = t.object_id
where t.name = 'TableName'
If you like me, needed to be able to do this for tables in an arbitrary database, then I found the following solution:
IF EXISTS (
SELECT 1
FROM [database name].sys.identity_columns AS id_col
INNER JOIN [database name].sys.objects
ON objects.object_id = id_col.object_id
INNER JOIN [database name].sys.schemas
ON schemas.schema_id = objects.schema_id
AND schemas.name = 'schema name'
WHERE OBJECT_NAME(id_col.object_id, DB_ID('database name')) = 'table name'
) SELECT 1 ELSE SELECT 0
you can get the 1 or 0 Boolean Form if the current table has identity Columns by using this
SELECT Count(Column_ID) FROM sys.identity_columns WHERE OBJECT_NAME(object_id) = 'tableName'
One way to list all Tables with their identity column if it exists
to get you desired table add at the end of the filter "and o.name='TableName'"
where Tbale Nam is the table you are looking for
SELECT o.[Name][TableName],i.[name][IdentityColName] FROM
sys.objects o
left outer join sys.identity_columns i on i.object_id=o.object_id
where o.type='U'