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

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

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]

Replacing a SQL Server subquery in a stored procedure with a string variable

CREATE PROCEDURE CreateAsset
#NumeratorQuery AS TEXT,
#DenominatorQuery AS TEXT,
#family AS VARCHAR(255),
#control_num AS VARCHAR(255),
#metric_sequence AS VARCHAR(255)
AS
-- Drop Temporary Tables if they exist
IF OBJECT_ID('tempdb.dbo.#TempNumerator') IS NOT NULL
DROP TABLE tempdb.dbo.#TempNumerator
IF OBJECT_ID('tempdb.dbo.#TempDenominator') IS NOT NULL
DROP TABLE tempdb.dbo.#TempDenominator
IF OBJECT_ID('tempdb.dbo.#TempAssets') IS NOT NULL
DROP TABLE tempdb.dbo.#TempAssets
IF OBJECT_ID('tempdb.dbo.#TempSummary') IS NOT NULL
DROP TABLE tempdb.dbo.#TempSummary
-- Create Temporary Numerator Table
SELECT *
INTO #TempNumerator
FROM
(SELECT DISTINCT AssetID
FROM openquery(RISKFABRIC_SACS_PROD, 'SELECT computer_name AS AssetID
FROM SACS_PROD.assets
WHERE (device_type = "SERVER" OR
device_type = "INDETERMINATE" OR
device_type = "VIRTUAL MACHINE")
AND (status = "ACTIVE (PER DOCUMENTATION)" OR status="ACTIVE (DEFINITIVE)")')
) AS #TempNumerator
.
.
.
In the above stored procedure, I want to replace everything inside the subquery with #NumeratorQuery like this:
SELECT *
INTO #TempNumerator
FROM (#NumeratorQuery) AS #TempNumerator
How does one accomplish this?
If I understand your question correctly, it will look like this:
Create Table #TempNumerator
(
field list...
)
insert into #TempNumerator
exec (#NumeratorQuery)
DECLARE#NumeratorQuery NVARCHAR(500)
SET#NumeratorQuery ='SELECT DISTINCT AssetID
FROM openquery(RISKFABRIC_SACS_PROD,'SELECT computer_name AS AssetID
FROM SACS_PROD.assets
WHERE(device_type="SERVER"OR
device_type="INDETERMINATE"OR
device_type="VIRTUAL MACHINE")
AND(status="ACTIVE (PER DOCUMENTATION)"OR status="ACTIVE (DEFINITIVE)")'
EXEC(#NumeratorQuery)
I found the real root cause of the problem. Putting EXEC in the FROM clause of a SELECT INTO statement is not supported in older versions of MSSQL, but it is supported in newer versions I've read.
SELECT *
INTO #TempNumerator
FROM EXEC(#NumeratorQuery) AS #TempNumerator
That's really the root cause; the same restriction is not present for the insert statement so thus, this works perfectly:
CREATE TABLE #TempNumerator (AssetID VARCHAR(255))
INSERT INTO #TempNumerator EXEC(#NumeratorQuery)

CREATE VIEW AS SELECT FROM with check if table exists

Is it possible to check, when creating a view, that the table I am selecting from actually exists? To prevent Invalid object name 'MyItems' or Incorrect syntax near keyword VIEW.
-- ok this is super simplistic
-- but it is enough to illustrate the point
CREATE VIEW vw_MyView
AS
SELECT DISTINCT Id, Name, COUNT(CategoryId) OVER (PARTITION BY Id) AS Total
FROM MyItems
GO
I have a bunch of these views that get created as part of the batch script. In this example, if MyItems doesn't exist the execution breaks the entire batch script.
So, I thought I would just add a bunch of checks in front of every CREATE VIEW statement to make sure the table(s) actually exist.
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[MyItems]') AND type IN (N'U'))
-- CREATE VIEW <snip></snip>
GO
But as it turns out the CREATE VIEW statement must be the first statement in a query batch.
Back my question, is there some kind of workaround for catching when CREATE VIEW fails to complete because it is reading from none existent table?
I am using MS SQL Server 2008 R2.
Another alternative using dynamic SQL:
DECLARE #sqlcmd NVARCHAR(MAX);
SELECT #sqlcmd =
'CREATE VIEW vw_MyView
AS
SELECT DISTINCT Id, Name, COUNT(CategoryId) OVER (PARTITION BY Id) AS Total
FROM MyItems
GO';
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[MyItems]') AND type IN (N'U'))
BEGIN
EXEC sp_executesql #sqlcmd
END
ELSE
BEGIN
PRINT 'Table doesnt exist'
END
This code May helps you,Try once
IF EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'MyItems'
AND TABLE_TYPE = 'BASE TABLE'
)
BEGIN
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'vw_MyView'
AND TABLE_TYPE = 'VIEW'
)
BEGIN
CREATE VIEW vw_MyView
AS
SELECT DISTINCT Id
,NAME
,COUNT(CategoryId) OVER (PARTITION BY Id) AS Total
FROM MyItems
END
END

SQL Query Delete rows from a table

I have a table #temp. The data in #temp are table names in a database. I wish to only show the table names of which the table has data. How can I do this without using dynamic SQL?
My sample data is as below:
create TABLE #temp (Table_Name VARCHAR(50))
insert into #temp values ('#temp1')
,('#temp2')
,('#temp3')
,('#temp4')
create TABLE #temp1 (Col1 int)
insert into #temp1 values (1)
,(3)
,(4)
create TABLE #temp2 (Col1 int)
insert into #temp2 values (7)
,(9)
,(6)
create TABLE #temp3 (Col1 int)
create TABLE #temp4 (Col1 int)
I manually delete the blank tables, How to do this using a query for numerous blank tables?
DELETE FROM #temp
WHERE Table_Name = '#temp3'
or Table_Name = '#temp4'
This is the result I want
select * from #temp
-- It only shows the two table names which are not blank
DROP TABLE #temp
DROP TABLE #temp1
DROP TABLE #temp2
DROP TABLE #temp3
DROP TABLE #temp4
This is my old query for this question:
DECLARE #TABLE_NAME VARCHAR(50), #COMMAND VARCHAR(500), #COUNT INT, #COUNTT INT
DECLARE #CountResults TABLE (CountReturned INT)
create TABLE #TABLE_NAME (TABLE_NAME VARCHAR(50))
SELECT #COUNTT= COUNT(*) FROM #temp
WHILE #COUNTT > 0
BEGIN
SELECT TOP 1 #TABLE_NAME = Table_Name FROM #temp
SET #COMMAND = 'SELECT COUNT(*) FROM ' + #TABLE_NAME
INSERT #CountResults EXEC (#COMMAND)
SET #Count = (SELECT * FROM #CountResults)
BEGIN TRANSACTION
DELETE #CountResults
ROLLBACK TRANSACTION
IF(#Count > 0)
BEGIN
INSERT INTO #TABLE_NAME VALUES (#TABLE_NAME)
END
DELETE FROM #temp WHERE Table_Name = #TABLE_NAME
SELECT #COUNTT= COUNT(*) FROM #temp
END
SELECT * FROM #TABLE_NAME
I don't know of any way to determine whether or not a table is empty without querying that table, which in your case means dynamic SQL. Your comments make it sound like you're okay with this but are looking for a way to do this more concisely than using a loop. Here's a (limited) possibility:
declare #sql nvarchar(max);
select #sql =
-- coalesce() ensures that UNION ALL is inserted before every SELECT but the first.
coalesce(#sql + N' union all ', N'') +
-- Select each table name. Note that SQL Server allows table names that contain
-- single quotes. In this case (or in the case of plain old bad/malicious data in
-- #temp), we need to make sure those characters are enclosed within the string
-- literal we're building.
N'select ''' + replace(table_name, N'''', N'''''') +
-- Use EXISTS to make sure there are one or more records in the table.
N''' where exists (select 1 from ' + quotename(table_name) + N')'
from #temp;
exec sp_executesql #sql;
This will build and execute a query that looks like this:
select '#temp1' where exists (select 1 from [#temp1])
union all
select '#temp2' where exists (select 1 from [#temp2])
union all
select '#temp3' where exists (select 1 from [#temp3])
union all
select '#temp4' where exists (select 1 from [#temp4])
This approach has a few limitations that you should be aware of:
The query will fail if #temp contains any string which is not the name of a table or view. Normally I'd suggest mitigating this by using object_id() or querying INFORMATION_SCHEMA.TABLES, but the fact that you've loaded #temp with the names of other temp tables complicates matters.
The query will also fail if #temp contains a table name that explicitly names the table schema, e.g. dbo.Stuff, because quotename() will render it as [dbo.Stuff] rather than [dbo].[Stuff]. But if you omit quotename(), you run the risk of incorrect and/or damaging behavior if a table_name contains spaces or other problematic characters.
In short, if you just want something for personal use and are okay with making certain assumptions about the data in #temp, then something like the above ought to work. But if you want something that will work correctly and safely under any circumstances, then it's going to take some doing, enough so that even if you could avoid using some kind of a loop, doing so is unlikely to make things any less complicated.
I have a method that does not use dynamic sql. It uses the sysindexes table, which according to Microsoft is subject to change at their whim. So this may not be a good candidate for a production system. But it could be a good place to start. This is also a bit easier if your source table is not a temp table, since temp tables have actual names that do not match the name used to create them.
This script worked for me on SQL Server 2008 r2.
-- drop table #MyTempTable;
Create table #MyTempTable(Table_Name varchar(50));
insert #MyTempTable values ('#MyTempTable2');
insert #MyTempTable values ('#MyTempTable3');
insert #MyTempTable values ('#MyTempTable4');
Create table #MyTempTable2 (Col1 int);
insert #MyTempTable2 values (1);
Create table #MyTempTable4 (Col1 int);
Create table #MyTempTable3 (Col1 int);
SELECT *
FROM #MyTempTable M1
JOIN tempdb.sys.tables T ON T.name LIKE (M1.Table_Name + '%')
JOIN [tempdb].[dbo].[sysindexes] S ON S.id = T.object_id
WHERE S.rowcnt > 0
It's not an ideal solution, but it satisfies your requirements. If you play around with it in your environment, it might give you some insight into a better way to achieve your larger goals. good luck.
EDIT: sysindexes will have one entry per index on the table. Or in the case of my example, for the heap (with no index.) So if your base tables have multiple indexes, you will need to modify the query a bit. Maybe change the JOIN and WHERE clause to a WHERE EXISTS SELECT * FROM [tempdb].[dbo].[sysindexes] S WHERE S.id = T.object_id AND S.rowcnt > 0 Play with it and you should be able to get where you were asking.
EDIT 2: Replacing sys.tables with sysobjects.
SELECT *
FROM #MyTempTable M1
JOIN [tempdb].[dbo].[sysobjects] O ON O.name LIKE (M1.Table_Name + '%')
JOIN [tempdb].[dbo].[sysindexes] S ON S.id = O.id
WHERE S.rowcnt > 0
Based on DeadZone's Query, the following works for non temp tables:
SELECT DISTINCT Table_Name
INTO #TABLE_NAME
FROM #Temp M1
JOIN [dbo].[sysobjects] O ON O.name LIKE (M1.Table_Name + '%')
JOIN [dbo].[sysindexes] S ON S.id = O.id
WHERE S.rowcnt > 0

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

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')

Resources