Check if table exists in SQL Server - sql-server

I would like this to be the ultimate discussion on how to check if a table exists in SQL Server 2000/2005 using SQL Statements.
Here are two possible ways of doing it. Which one is the standard/best way of doing it?
First way:
IF EXISTS (SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE'
AND TABLE_NAME='mytablename')
SELECT 1 AS res ELSE SELECT 0 AS res;
Second way:
IF OBJECT_ID (N'mytablename', N'U') IS NOT NULL
SELECT 1 AS res ELSE SELECT 0 AS res;
MySQL provides the simple
SHOW TABLES LIKE '%tablename%';
statement. I am looking for something similar.

For queries like this it is always best to use an INFORMATION_SCHEMA view. These views are (mostly) standard across many different databases and rarely change from version to version.
To check if a table exists use:
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'TheSchema'
AND TABLE_NAME = 'TheTable'))
BEGIN
--Do Stuff
END

Also note that if for any reason you need to check for a temporary table you can do this:
if OBJECT_ID('tempdb..#test') is not null
--- temp table exists

We always use the OBJECT_ID style for as long as I remember
IF OBJECT_ID('*objectName*', 'U') IS NOT NULL

Please see the below approaches,
Approach 1: Using INFORMATION_SCHEMA.TABLES view
We can write a query like below to check if a Customers Table exists in the current database.
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'Customers')
BEGIN
PRINT 'Table Exists'
END
Approach 2: Using OBJECT_ID() function
We can use OBJECT_ID() function like below to check if a Customers Table exists in the current database.
IF OBJECT_ID(N'dbo.Customers', N'U') IS NOT NULL
BEGIN
PRINT 'Table Exists'
END
Approach 3: Using sys.Objects Catalog View
We can use the Sys.Objects catalog view to check the existence of the Table as shown below:
IF EXISTS(SELECT 1 FROM sys.Objects WHERE Object_id = OBJECT_ID(N'dbo.Customers') AND Type = N'U')
BEGIN
PRINT 'Table Exists'
END
Approach 4: Using sys.Tables Catalog View
We can use the Sys.Tables catalog view to check the existence of the Table as shown below:
IF EXISTS(SELECT 1 FROM sys.Tables WHERE Name = N'Customers' AND Type = N'U')
BEGIN
PRINT 'Table Exists'
END
Approach 5: Avoid Using sys.sysobjects System table
We should avoid using sys.sysobjects System Table directly, direct access to it will be deprecated in some future versions of the Sql Server. As per Microsoft BOL link, Microsoft is suggesting to use the catalog views sys.objects/sys.tables instead of sys.sysobjects system table directly.
IF EXISTS(SELECT name FROM sys.sysobjects WHERE Name = N'Customers' AND xtype = N'U')
BEGIN
PRINT 'Table Exists'
END
referred from: http://sqlhints.com/2014/04/13/how-to-check-if-a-table-exists-in-sql-server/

Looking for a table on a different database:
if exists (select * from MyOtherDatabase.sys.tables where name = 'MyTable')
print 'Exists'

Just wanted to mention one situation where it would probably be a little easier to use the OBJECT_ID method. The INFORMATION_SCHEMA views are objects under each database-
The information schema views are defined in a special schema named
INFORMATION_SCHEMA. This schema is contained in each database.
https://msdn.microsoft.com/en-us/library/ms186778.aspx
Therefore all tables you access using
IF EXISTS (SELECT 1
FROM [database].INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE'
AND TABLE_NAME='mytablename')
SELECT 1 AS res ELSE SELECT 0 AS res;
will only reflect what is in [database]. If you wanted to check if tables in another database exist, without dynamically changing the [database] each time, OBJECT_ID will let you do this out of the box. Ex-
IF OBJECT_ID (N'db1.schema.table1', N'U') IS NOT NULL
SELECT 1 AS res ELSE SELECT 0 AS res;
works just as well as
IF OBJECT_ID (N'db2.schema.table1', N'U') IS NOT NULL
SELECT 1 AS res ELSE SELECT 0 AS res;
SQL SERVER 2016 Edit:
Starting with 2016, Microsoft simplified the ability to check for non-existent objects prior to dropping, by adding the if exists keywords to drop statements. For example,
drop table if exists mytablename
will do the same thing as OBJECT_ID / INFORMATION_SCHEMA wrappers, in 1 line of code.
https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/

IF OBJECT_ID('mytablename') IS NOT NULL

Using the Information Schema is the SQL Standard way to do it, so it should be used by all databases that support it. See Approach 1 in this answer.

You can use below code
IF (OBJECT_ID('TableName') IS NOT NULL )
BEGIN
PRINT 'Table Exists'
END
ELSE
BEGIN
PRINT 'Table NOT Exists'
END
Or
IF (EXISTS (SELECT * FROM sys.tables WHERE [name] = 'TableName'))
BEGIN
PRINT 'Table Exists'
END
ELSE
BEGIN
PRINT 'Table NOT Exists'
END

IF EXISTS
(
SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[Mapping_APCToFANavigator]')
AND
type in (N'U')
)
BEGIN
-- Do whatever you need to here.
END
Here in the above code, the table name is Mapping_APCToFANavigator.

If you need to work on different databases:
DECLARE #Catalog VARCHAR(255)
SET #Catalog = 'MyDatabase'
DECLARE #Schema VARCHAR(255)
SET #Schema = 'dbo'
DECLARE #Table VARCHAR(255)
SET #Table = 'MyTable'
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_CATALOG = #Catalog
AND TABLE_SCHEMA = #Schema
AND TABLE_NAME = #Table))
BEGIN
--do stuff
END

I know it is an old question but I have found this possibility if you plan to call it often.
create procedure Table_Exists
#tbl varchar(50)
as
return (select count(*) from sysobjects where type = 'U' and name = #tbl)
go

Just adding here, for the benefit of developers and fellow DBAs
a script that receives #Tablename as a parameter
(which may or may not contain the schemaname) and returns the info below if the schema.table exists:
the_name object_id the_schema the_table the_type
[Facts].[FactBackOrder] 758293761 Facts FactBackOrder Table
I produced this script to be used inside other scripts every time I need to test whether or not a table or view exists, and when it does, get its object_id to be used for other purposes.
It raises an error when either you passed an empty string, wrong schema name or wrong table name.
this could be inside a procedure and return -1 for example.
As an example, I have a table called "Facts.FactBackOrder" in one of my Data Warehouse databases.
This is how I achieved this:
PRINT 'THE SERVER IS ' + ##SERVERNAME
--select db_name()
PRINT 'THE DATABASE IS ' + db_NAME()
PRINT ''
GO
SET NOCOUNT ON
GO
--===================================================================================
-- #TableName is the parameter
-- the object we want to deal with (it might be an indexed view or a table)
-- the schema might or might not be specified
-- when not specified it is DBO
--===================================================================================
DECLARE #TableName SYSNAME
SELECT #TableName = 'Facts.FactBackOrder'
--===================================================================================
--===================================================================================
DECLARE #Schema SYSNAME
DECLARE #I INT
DECLARE #Z INT
SELECT #TableName = LTRIM(RTRIM(#TableName))
SELECT #Z = LEN(#TableName)
IF (#Z = 0) BEGIN
RAISERROR('Invalid #Tablename passed.',16,1)
END
SELECT #I = CHARINDEX('.',#TableName )
--SELECT #TableName ,#I
IF #I > 0 BEGIN
--===================================================================================
-- a schema and table name have been passed
-- example Facts.FactBackOrder
-- #Schema = Fact
-- #TableName = FactBackOrder
--===================================================================================
SELECT #Schema = SUBSTRING(#TABLENAME,1,#I-1)
SELECT #TableName = SUBSTRING(#TABLENAME,#I+1,#Z-#I)
END
ELSE BEGIN
--===================================================================================
-- just a table name have been passed
-- so the schema will be dbo
-- example Orders
-- #Schema = dbo
-- #TableName = Orders
--===================================================================================
SELECT #Schema = 'DBO'
END
--===================================================================================
-- Check whether the #SchemaName is valid in the current database
--===================================================================================
IF NOT EXISTS ( SELECT * FROM INFORMATION_SCHEMA.SCHEMATA K WHERE K.[SCHEMA_NAME] = #Schema ) BEGIN
RAISERROR('Invalid Schema Name.',16,1)
END
--SELECT #Schema as [#Schema]
-- ,#TableName as [#TableName]
DECLARE #R1 TABLE (
THE_NAME SYSNAME
,THE_SCHEMA SYSNAME
,THE_TABLE SYSNAME
,OBJECT_ID INT
,THE_TYPE SYSNAME
,PRIMARY KEY CLUSTERED (THE_SCHEMA,THE_NAME)
)
;WITH RADHE_01 AS (
SELECT QUOTENAME(SCHEMA_NAME(O.schema_id)) + '.' + QUOTENAME(O.NAME) AS [the_name]
,the_schema=SCHEMA_NAME(O.schema_id)
,the_table=O.NAME
,object_id =o.object_id
,[the_type]= CASE WHEN O.TYPE = 'U' THEN 'Table' ELSE 'View' END
from sys.objects O
where O.is_ms_shipped = 0
AND O.TYPE IN ('U','V')
)
INSERT INTO #R1 (
THE_NAME
,THE_SCHEMA
,THE_TABLE
,OBJECT_ID
,THE_TYPE
)
SELECT the_name
,the_schema
,the_table
,object_id
,the_type
FROM RADHE_01
WHERE the_schema = #Schema
AND the_table = #TableName
IF (##ROWCOUNT = 0) BEGIN
RAISERROR('Invalid Table Name.',16,1)
END
ELSE BEGIN
SELECT THE_NAME
,THE_SCHEMA
,THE_TABLE
,OBJECT_ID
,THE_TYPE
FROM #R1
END

In SQL Server 2000 you can try:
IF EXISTS(SELECT 1 FROM sysobjects WHERE type = 'U' and name = 'MYTABLENAME')
BEGIN
SELECT 1 AS 'res'
END

IF EXISTS
(
SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'PutSchemaHere'
AND
TABLE_NAME = 'PutTableNameHere'
)

Something important to know for anybody who hasn't found their solution yet:
SQL server != MYSQL.
If you want to do it with MYSQL, it is quite simple
$sql = "SELECT 1 FROM `db_name`.`table_name` LIMIT 1;";
$result = mysql_query($sql);
if( $result == false )
echo "table DOES NOT EXIST";
else
echo "table exists";
Posting this here because it's the top hit at Google.

I've had some problems either with selecting from INFORMATIONAL_SCHEME and OBJECT_ID. I don't know if it's an issue of ODBC driver or something.. Queries from SQL management studio, both, were okay.
Here is the solution:
SELECT COUNT(*) FROM <yourTableNameHere>
So, if the query fails, there is, probably, no such table in the database (or you don't have access permissions to it).
The check is done by comparing the value (integer in my case) returned by SQL executor which deals with ODBC driver..
if (sqlexec(conectionHandle, 'SELECT COUNT(*) FROM myTable') == -1) {
// myTable doesn't exist..
}

IF EXISTS (
SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_CATALOG = 'Database Name' and
TABLE_NAME = 'Table Name' and
TABLE_SCHEMA = 'Schema Name') -- Database and Schema name in where statement can be deleted
BEGIN
--TABLE EXISTS
END
ELSE BEGIN
--TABLE DOES NOT EXISTS
END

You can use this :
IF OBJECT_ID (N'dbo.T', N'U') IS NOT NULL
BEGIN
print 'deleted table';
drop table t
END
else
begin
print 'table not found'
end
Create table t (id int identity(1,1) not null, name varchar(30) not null, lastname varchar(25) null)
insert into t( name, lastname) values('john','doe');
insert into t( name, lastname) values('rose',NULL);
Select * from t
1 john doe
2 rose NULL
-- clean
drop table t

I think the following query works:
IF EXISTS (select * from sys.tables
WHERE name='mytablename' )
BEGIN
print 'table exists in the database'
END

IF EXISTS ( SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'dbo.TableName') AND OBJECTPROPERTY(id, N'IsUserTable') = 1 )
BEGIN
SELECT * FROM dbo.TableName;
END
GO

There is one more option to check if the table exists across databases
IF EXISTS(SELECT 1 FROM [change-to-your-database].SYS.TABLES WHERE NAME = 'change-to-your-table-name')
BEGIN
-- do whatever you want
END

If anyone is trying to do this same thing in linq to sql (or especially linqpad) turn on option to include system tables and views and do this code:
let oSchema = sys.Schemas.FirstOrDefault(s=>s.Name==a.schema )
where oSchema !=null
let o=oSchema!=null?sys.Objects.FirstOrDefault (o => o.Name==a.item && o.Schema_id==oSchema.Schema_id):null
where o!=null
given that you have an object with the name in a property called item, and the schema in a property called schema where the source variable name is a

select name from SysObjects where xType='U' and name like '%xxx%' order by name

If this is to be the 'ultimate' discussion, then it should be noted that Larry Leonard's script can query a remote server as well if the servers are linked.
if exists (select * from REMOTE_SERVER.MyOtherDatabase.sys.tables where name = 'MyTable')
print 'Exists'

-- -- create procedure to check if a table exists
DELIMITER $$
DROP PROCEDURE IF EXISTS `checkIfTableExists`;
CREATE PROCEDURE checkIfTableExists(
IN databaseName CHAR(255),
IN tableName CHAR(255),
OUT boolExistsOrNot CHAR(40)
)
BEGIN
SELECT count(*) INTO boolExistsOrNot FROM information_schema.TABLES
WHERE (TABLE_SCHEMA = databaseName)
AND (TABLE_NAME = tableName);
END $$
DELIMITER ;
-- -- how to use : check if table migrations exists
CALL checkIfTableExists('muDbName', 'migrations', #output);

i taking here creating a view as example.
Because ALTER/CREATE commands can't be within BEGIN/END blocks. You need to test for existence and the drop it before doing a create
IF Object_ID('TestView') IS NOT NULL
DROP VIEW TestView
GO
CREATE VIEW TestView
as
. . .
GO
If you are woried about the permissions being lost you can script the GRANT statements as well and re-run those at the end.
You could wrap the create/alter into a string and do an EXEC - that might get ugly for large views
DECLARE #SQL as varchar(4000)
-- set to body of view
SET #SQL = 'SELECT X, Y, Z FROM TABLE'
IF Object_ID('TestView') IS NULL
SET #SQL = 'CREATE VIEW TestView AS ' + #SQL
ELSE
SET #SQL = 'ALTER VIEW TestView AS ' + #SQL

Run this query to check if the table exists in the database:
IF(SELECT TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'YourTableName') IS NOT NULL
PRINT 'Table Exists';

consider in one database you have a table t1. you want to run script on other Database like - if t1 exist then do nothing else create t1.
To do this open visual studio and do the following:
Right click on t1, then Script table as, then DROP and Create To, then New Query Editor
you will find your desired query. But before executing that script don't forget to comment out the drop statement in the query as you don't want to create new one if there is already one.
Thanks

Related

How to add comments in SQL Server [duplicate]

I need to create some pretty big tables in SQL Server 2008. While I do have SQL Server Management Studio (SSMS), I would like to comment the tables and the columns when I create the table. How do I do this?
An example of the query I am running:
CREATE TABLE cert_Certifications
(
certificationID int PRIMARY KEY IDENTITY,
profileID int,
cprAdultExp datetime null
)
I've tried COMMENT'Expiration Date for the Adult CPR' and COMMENT='Expiration Date for the Adult CPR' after the data type, and SQL Server is giving me an error.
This is what I use
/*==============================================================*/
/* Table: TABLE_1 */
/*==============================================================*/
create table TABLE_1 (
ID int identity,
COLUMN_1 varchar(10) null,
COLUMN_2 varchar(10) null,
constraint PK_TABLE_1 primary key nonclustered (ID)
)
go
declare #CurrentUser sysname
select #CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'This is my table comment',
'user', #CurrentUser, 'table', 'TABLE_1'
go
declare #CurrentUser sysname
select #CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'This is the primary key comment',
'user', #CurrentUser, 'table', 'TABLE_1', 'column', 'ID'
go
declare #CurrentUser sysname
select #CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'This is column one comment',
'user', #CurrentUser, 'table', 'TABLE_1', 'column', 'COLUMN_1'
go
declare #CurrentUser sysname
select #CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description',
'This is column 2 comment',
'user', #CurrentUser, 'table', 'TABLE_1', 'column', 'COLUMN_2'
go
You can put comments on both tables and columns by creating what are called Extended Properties. You can put extended properties at both the table level and column level. This can be done via T-SQL or SSMS.
For example, in T-SQL it looks something like this:
sp_addextendedproperty 'BackColor', 'Red', 'user', '<schema name>', 'table', '<table name', 'column', '<column name>'.
You can read more about it on sp_addextendedproperty (Transact-SQL).
I prefer the GUI when designing tables because I can visualize the layout better. In the GUI designer, one can add a description for the table and columns in the properties window as shown in the image below
There are good answers in this post. Adding that the value 'MS_Description' could be other thing. For example, we can use 'SourceDescription' for details about the source the data, 'TableDescription' for table and 'ColumnDescription' for each column on table.
Example:
-- Create example table
create table testTablename(
id int,
name varchar(20),
registerNumber bigint
)
-- SourceDescription
EXEC sys.sp_addextendedproperty
#name=N'SourceDescription',
#value=N'Result of process x union y ' , -- Comment about the source this data.
#level0type=N'SCHEMA',
#level0name=N'dbo',
#level1type=N'TABLE',
#level1name=N'testTableName' -- Name of Table
-- TableDescription
EXEC sys.sp_addextendedproperty
#name=N'TableDescription',
#value=N'Table is used for send email to clients.' , -- Coment about the used of table
#level0type=N'SCHEMA',
#level0name=N'dbo',
#level1type=N'TABLE',
#level1name=N'testTableName'
-- ColumnDescription
EXECUTE sp_addextendedproperty
#name = 'ColumnDescription',
#value = 'Unique identification of employer. Its the registry of company too.',
#level0type = 'SCHEMA',
#level0name= N'dbo',
#level1type = N'TABLE',
#level1name = N'testTableName',
#level2type = N'COLUMN',
#level2name = N'registerNumber'
-- If necessary, you can delete the comment.
exec sp_dropextendedproperty
#name = 'ColumnDescription',
#level0type = 'SCHEMA',
#level0name= N'dbo',
#level1type = N'TABLE',
#level1name = N'testTableName',
#level2type = N'COLUMN',
#level2name = N'registerNumber'
-- Show you the table resume
select
tables.name tableName,
tables.create_date,
tables.modify_date,
tableDesc.value TableDescription,
sourceDesc.value SourceDescription
from
sys.tables
left join sys.extended_properties tableDesc on tables.object_id = tableDesc.major_id and tableDesc.name = 'TableDescription'
left join sys.extended_properties sourceDesc on tables.object_id = sourceDesc.major_id and sourceDesc.name = 'SourceDescription'
where
tableDesc.name in('TableDescription', 'SourceDescription', 'ColumnDescription')
order by tables.name
-- show you the columns resume
select
tables.name tableName,
columns.name columnName,
extended_properties.value
from
sys.tables
inner join sys.columns on tables.object_id = columns.object_id
left join sys.extended_properties on
tables.object_id = extended_properties.major_id
and columns.column_id = extended_properties.minor_id
and extended_properties.name in('MS_Description','ColumnDescription')
where
tables.name = 'testTableName'
You need to use the stored procedure called sp_addextendedproperty to add comments to columns/tables in SQL Server.
Although it does not directly answer original question (J Henzel and Randy Minder already did!) I would like to share something else I just wrote that can be very useful for those who have to comment a lot of tables and columns.
The following queries:
-- Generate comments templates for all tables
SELECT
'EXEC sys.sp_addextendedproperty
#name=N''TableDescription'',
#level0type=N''SCHEMA'',
#level1type=N''TABLE'',
#level0name=N''' + TABLE_SCHEMA + ''',
#level1name=N''' + TABLE_NAME + ''',
#value=N''TODO'';'
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE'
AND TABLE_NAME NOT like 'sys%'
order by TABLE_SCHEMA, TABLE_NAME
-- Generate comments templates for all columns
SELECT 'EXECUTE sp_addextendedproperty
#name = ''ColumnDescription'',
#level0type = ''SCHEMA'',
#level1type = N''TABLE'',
#level2type = N''COLUMN'',
#level0name=N''' + TABLE_SCHEMA + ''',
#level1name=N''' + TABLE_NAME + ''',
#level2name = N''' + COLUMN_NAME + ''',
#value = ''TODO'';'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA not like 'sys%' and TABLE_NAME not like 'sysdiagrams%'
order by TABLE_SCHEMA, TABLE_NAME, case when ORDINAL_POSITION = 1 then '0' else COLUMN_NAME end
Will produce in SQL Server output a list of calls to sp_addextendedproperty for all the tables and all the columns existing in your database, by querying on system tables to gather them.
Of course, it will not comment it magically for you, but at least you just have to fill the "TODOs" placeholders with the relevant comment for all objects you would like to describe and to execute it.
It avoids you to manually write all the calls and saves a lot of time, and with it you can't forget a table or column, so I hope it will be useful for somebody else.
Side remarks:
Just beware on the filters in WHEREs on "sys": it's here to exclude system objects, but depending on your objects names, you may need a bit of fine-tuning of you have tables named alike.
Also, there isn't any comment at all in my DB, so my query returns all tables/columns. It does not consider whether there's already a comment or not on it.
Use this SQL command:
Create table TABLE NAME (ATTRIBUTE NAME (ATTRIBUTE SIZE)) // Both create and table are keywords

UPSERT into sql server from an Excel File

I have a SP that runs everynight to Insert and Update the content of a table based on an excel file (Excel 2010 on Windows Server 20008 R2). Below is my SP and the image represents my table's structure and the excel file format. I just need to double check my SP with you guys to make sure I am doing this correctly and if I am on the right track. The excel file includes 3 columns both Cust_Num and Cust_Seq are primary since there would never be a case that same combination of Cust_Num and Cust_Seq exist for a customer name. For example, for Cust_Num = 1 and Cust_Num=0 there will never be another of same combination of Cust_Num being 1 and Cust_Num being 0. However the name will usually repeat in the spreadsheet. So, would you guys please let me know if the SP is correct or not? (in the SP first the Insert statement runs and then the Update Statement):
**First The Insert runs in the SP
INSERT INTO Database.dbo.Routing_CustAddress
SELECT a.[Cust Num],a.[Cust Seq],a.[Name]
FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',
'Excel 8.0;HDR=YES;Database=C:\Data\custaddr.xls;',
'SELECT*
FROM [List_Frame_1$]') a Left join Routing_CustAddress b
on a.[Cust Num] = b.Cust_Num and a.[Cust Seq] = b.Cust_Seq where b.Cust_Num is null
***Then the Update Runs in the SP
UPDATE SPCustAddress
SET SPCustAddress.Name = CustAddress.Name
FROM ArPd_App.dbo.Routing_CustAddress SPCustAddress
INNER JOIN OPENROWSET('Microsoft.ACE.OLEDB.12.0',
'Excel 8.0;HDR=YES;Database=C:\Data\custaddr.xls;',
'SELECT *
FROM [List_Frame_1$]')CustAddress
ON SPCustAddress.Cust_Num = CustAddress.[Cust Num]
AND SPCustAddress.Cust_Seq = CustAddress.[Cust Seq]
Right here is some code I havent tested it so I'll leave it for you but it shold work
Create the stagging table first.
CREATE TABLE dbo.Routing_CustAddress_Stagging
(
Cust_Name NVARCHAR(80),
Cust_Seq NVARCHAR(80),
Name NVARCHAR(MAX)
)
GO
Then create the following Stored Procedure. It will take the FilePath and Sheet name as parameter and does the whole lot for you.
1) TRUNCATE the stagging table.
2) Upload data into stagging table from provided Excel file, and sheet.
3) and finnaly does the UPSERT operation in two separate statements.
CREATE PROCEDURE usp_Data_Upload_Via_File
#FilePath NVARCHAR(MAX),
#SheetName NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
IF (#FilePath IS NULL OR #SheetName IS NULL)
BEGIN
RAISERROR('Please Provide valid File Path and SheetName',16,1)
RETURN;
END
-- Truncate the stagging table first
TRUNCATE TABLE dbo.Routing_CustAddress_Stagging;
-- Load Data from Excel sheet
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' INSERT INTO dbo.Routing_CustAddress_Stagging ([Cust Num],[Cust Seq],[Name]) ' +
N' SELECT [Cust Num],[Cust Seq],[Name] ' +
N' FROM OPENROWSET(''Microsoft.ACE.OLEDB.12.0'', ' +
N' ''Excel 8.0;HDR=YES;Database='+ #FilePath + ';'' ,' +
N' ''SELECT* FROM ['+ #SheetName +']'')'
EXECUTE sp_executesql #Sql
-- Now the UPSERT statement.
UPDATE T
SET T.Name = ST.NAME
FROM dbo.Routing_CustAddress T INNER JOIN dbo.Routing_CustAddress_Stagging ST
ON T.Cust_Name = ST.Cust_Name AND T.Cust_Seq = ST.Cust_Seq
-- Now the Insert Statement
INSERT INTO dbo.Routing_CustAddress
SELECT ST.[Cust Num],ST.[Cust Seq],ST.[Name]
FROM dbo.Routing_CustAddress_Stagging ST LEFT JOIN dbo.Routing_CustAddress T
ON T.Cust_Name = ST.Cust_Name AND T.Cust_Seq = ST.Cust_Seq
WHERE T.Cust_Name IS NULL OR T.Cust_Seq IS NULL
END

Retrieve column definition for stored procedure result set

I'm working with stored procedures in SQL Server 2008 and I've come to learn that I have to INSERT INTO a temp table that has been predefined in order to work with the data. That's fine, except how do I figure out how to define my temp table, if I'm not the one that wrote the stored procedure other than listing its definition and reading through the code?
For example, what would my temporary table look like for `EXEC sp_stored_procedure'? That is a simple stored procedure, and I could probably guess at the data types, but it seems there must be a way to just read the type and length of the columns returned from executing the procedure.
So let's say you have a stored procedure in tempdb:
USE tempdb;
GO
CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
SET NOCOUNT ON;
SELECT foo = 1, bar = 'tooth';
END
GO
There is a quite convoluted way you can go about determining the metadata that the stored procedure will output. There are several caveats, including the procedure can only output a single result set, and that a best guess will be made about the data type if it can't be determined precisely. It requires the use of OPENQUERY and a loopback linked server with the 'DATA ACCESS' property set to true. You can check sys.servers to see if you already have a valid server, but let's just create one manually called loopback:
EXEC master..sp_addlinkedserver
#server = 'loopback',
#srvproduct = '',
#provider = 'SQLNCLI',
#datasrc = ##SERVERNAME;
EXEC master..sp_serveroption
#server = 'loopback',
#optname = 'DATA ACCESS',
#optvalue = 'TRUE';
Now that you can query this as a linked server, you can use the result of any query (including a stored procedure call) as a regular SELECT. So you can do this (note that the database prefix is important, otherwise you will get error 11529 and 2812):
SELECT * FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
If we can perform a SELECT *, we can also perform a SELECT * INTO:
SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
And once that #tmp table exists, we can determine the metadata by saying (assuming SQL Server 2005 or greater):
SELECT c.name, [type] = t.name, c.max_length, c.[precision], c.scale
FROM sys.columns AS c
INNER JOIN sys.types AS t
ON c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE c.[object_id] = OBJECT_ID('tempdb..#tmp');
(If you're using SQL Server 2000, you can do something similar with syscolumns, but I don't have a 2000 instance handy to validate an equivalent query.)
Results:
name type max_length precision scale
--------- ------- ---------- --------- -----
foo int 4 10 0
bar varchar 5 0 0
In Denali, this will be much, much, much easier. Again there is still a limitation of the first result set but you don't have to set up a linked server and jump through all those hoops. You can just say:
DECLARE #sql NVARCHAR(MAX) = N'EXEC tempdb.dbo.my_procedure;';
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set(#sql, NULL, 1);
Results:
name system_type_name
--------- ----------------
foo int
bar varchar(5)
Until Denali, I suggest it would be easier to just roll up your sleeves and figure out the data types on your own. Not just because it's tedious to go through the above steps, but also because you are far more likely to make a correct (or at least more accurate) guess than the engine will, since the data type guesses the engine makes will be based on runtime output, without any external knowledge of the domain of possible values. This factor will remain true in Denali as well, so don't get the impression that the new metadata discovery features are a be-all end-all, they just make the above a bit less tedious.
Oh and for some other potential gotchas with OPENQUERY, see Erland Sommarskog's article here:
http://www.sommarskog.se/share_data.html#OPENQUERY
It looks like in SQL 2012 there is a new SP to help with this.
exec sp_describe_first_result_set N'PROC_NAME'
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-describe-first-result-set-transact-sql
A less sophisticated way (that could be sufficient in some cases): edit your original SP, after the final SELECT and before the FROM clause add INSERT INTO tmpTable to save the SP result in tmpTable.
Run the modified SP, preferably with meaningful parameters in order to get actual data. Restore the original code of the procedure.
Now you can get the script of tmpTable from SQL server management studio or query sys.columns to get fields descriptions.
Here is some code that I wrote. The idea is (as someone else stated) is to get the SP code, modify it and execute it. However, my code does not change the original SP.
First step, get the definition of the SP, strip the 'Create' part out and get rid of the 'AS' after the declaration of parameters, if exists.
Declare #SPName varchar(250)
Set nocount on
Declare #SQL Varchar(max), #SQLReverse Varchar(MAX), #StartPos int, #LastParameterName varchar(250) = '', #TableName varchar(36) = 'A' + REPLACE(CONVERT(varchar(36), NewID()), '-', '')
Select * INTO #Temp from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME = 'ADMIN_Sync_CompareDataForSync'
if ##ROWCOUNT > 0
BEGIN
Select #SQL = REPLACE(ROUTINE_DEFINITION, 'CREATE PROCEDURE [' + ROUTINE_SCHEMA + '].[' + ROUTINE_NAME + ']', 'Declare')
from INFORMATION_SCHEMA.ROUTINES
where ROUTINE_NAME = #SPName
Select #LastParameterName = PARAMETER_NAME + ' ' + DATA_TYPE +
CASE WHEN CHARACTER_MAXIMUM_LENGTH is not null THEN '(' +
CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN 'MAX' ELSE CONVERT(varchar,CHARACTER_MAXIMUM_LENGTH) END + ')' ELSE '' END
from #Temp
WHERE ORDINAL_POSITION =
(Select MAX(ORDINAL_POSITION)
From #Temp)
Select #StartPos = CHARINDEX(#LastParameterName, REPLACE(#SQL, ' ', ' '), 1) + LEN(#LastParameterName)
END
else
Select #SQL = REPLACE(ROUTINE_DEFINITION, 'CREATE PROCEDURE [' + ROUTINE_SCHEMA + '].[' + ROUTINE_NAME + ']', '') from INFORMATION_SCHEMA.ROUTINES where ROUTINE_NAME = #SPName
DROP TABLE #Temp
Select #StartPos = CHARINDEX('AS', UPPER(#SQL), #StartPos)
Select #SQL = STUFF(#SQL, #StartPos, 2, '')
(Note the creation of a new table name based on a unique identifier)
Now find the last 'From' word in the code assuming this is the code that does the select that returns the result set.
Select #SQLReverse = REVERSE(#SQL)
Select #StartPos = CHARINDEX('MORF', UPPER(#SQLReverse), 1)
Change the code to select the resultset into a table (the table based on the uniqueidentifier)
Select #StartPos = LEN(#SQL) - #StartPos - 2
Select #SQL = STUFF(#SQL, #StartPos, 5, ' INTO ' + #TableName + ' FROM ')
EXEC (#SQL)
The result set is now in a table, it does not matter if the table is empty!
Lets get the structure of the table
Select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #TableName
You can now do your magic with this
Don't forget to drop that unique table
Select #SQL = 'drop table ' + #TableName
Exec (#SQL)
Hope this helps!
In order to get queryable resultset sys.dm_exec_describe_first_result_set(SQL Server 2012) could be used:
SELECT column_ordinal, name, system_type_name
FROM sys.dm_exec_describe_first_result_set(N'EXEC stored_procedure_name', NULL, 0);
db<>fiddle demo
This soultion has few limitations though for instance SP cannot use temporary tables.
If you are working in an environment with restricted rights where things like loopback linked server seems black magic and are definitely "no way!", but you have a few rights on schema and only a couple of stored procedure to process there is a very simple solution.
You can use the very helpful SELECT INTO syntax, which will create a new table with result set of a query.
Let's say your procedure contains the following Select query :
SELECT x, y, z
FROM MyTable t INNER JOIN Table2 t2 ON t.id = t2.id...
Instead replace it by :
SELECT x, y, z
INTO MyOutputTable
FROM MyTable t INNER JOIN Table2 t2 ON t.id = t2.id...
When you will execute it, it will create a new table MyOutputTable with the results returned by the query.
You just have to do a right click on its name to get the table definition.
That's all !
SELECT INTO only require the ability to create new tables and also works with temporary tables (SELECT... INTO #MyTempTable), but it could be harder to retrieve the definition.
However of course if you need to retrieve the output definition of a thousands SP, it's not the fastest way :)

Insert/Update/Delete with function in SQL Server

Can we perform Insert/Update/Delete statement with SQL Server Functions. I have tried with but SQL Server error is occured.
Error:
Invalid use of side-effecting or time-dependent operator in 'DELETE' within a function.
AnyBody have any Idea why we can not use Insert/Update/Delete statements with SQL Server functions.
Waiting for your good idea's
No, you cannot.
From SQL Server Books Online:
User-defined functions cannot be used
to perform actions that modify the
database state.
Ref.
Yes, you can!))
Disclaimer: This is not a solution, it is more of a hack to test out something. User-defined functions cannot be used to perform actions that modify the database state.
I found one way to make INSERT, UPDATE or DELETE in function using xp_cmdshell.
So you need just to replace the code inside #sql variable.
CREATE FUNCTION [dbo].[_tmp_func](#orderID NVARCHAR(50))
RETURNS INT
AS
BEGIN
DECLARE #sql varchar(4000), #cmd varchar(4000)
SELECT #sql = 'INSERT INTO _ord (ord_Code) VALUES (''' + #orderID + ''') '
SELECT #cmd = 'sqlcmd -S ' + ##servername +
' -d ' + db_name() + ' -Q "' + #sql + '"'
EXEC master..xp_cmdshell #cmd, 'no_output'
RETURN 1
END
Functions in SQL Server, as in mathematics, can not be used to modify the database. They are intended to be read only and can help developer to implement command-query separation. In other words, asking a question should not change the answer. When your program needs to modify the database use a stored procedure instead.
You can't update tables from a function like you would a stored procedure, but you CAN update table variables.
So for example, you can't do this in your function:
create table MyTable
(
ID int,
column1 varchar(100)
)
update [MyTable]
set column1='My value'
but you can do:
declare #myTable table
(
ID int,
column1 varchar(100)
)
Update #myTable
set column1='My value'
Yes, you can.
However, it requires SQL CLR with EXTERNAL_ACCESS or UNSAFE permission and specifying a connection string. This is obviously not recommended.
For example, using Eval SQL.NET (a SQL CLR which allow to add C# syntax in SQL)
CREATE FUNCTION [dbo].[fn_modify_table_state]
(
#conn VARCHAR(8000) ,
#sql VARCHAR(8000)
)
RETURNS INT
AS
BEGIN
RETURN SQLNET::New('
using(var connection = new SqlConnection(conn))
{
connection.Open();
using(var command = new SqlCommand(sql, connection))
{
return command.ExecuteNonQuery();
}
}
').ValueString('conn', #conn).ValueString('sql', #sql).EvalReadAccessInt()
END
GO
DECLARE #conn VARCHAR(8000) = 'Data Source=XPS8700;Initial Catalog=SqlServerEval_Debug;Integrated Security=True'
DECLARE #sql VARCHAR(8000) = 'UPDATE [Table_1] SET Value = -1 WHERE Name = ''zzz'''
DECLARE #rowAffecteds INT = dbo.fn_modify_table_state(#conn, #sql)
Documentation: Modify table state within a SQL Function
Disclaimer: I'm the owner of the project Eval SQL.NET
You can have a table variable as a return type and then update or insert on a table based on that output.
In other words, you can set the variable output as the original table, make the modifications and then do an insert to the original table from function output.
It is a little hack but if you insert the #output_table from the original table and then say for example:
Insert into my_table
select * from my_function
then you can achieve the result.
We can't say that it is possible of not their is some other way exist to perform update operation in user-defined Function. Directly DML is not possible in UDF it is for sure.
Below Query is working perfectly:
create table testTbl
(
id int identity(1,1) Not null,
name nvarchar(100)
)
GO
insert into testTbl values('ajay'),('amit'),('akhil')
Go
create function tblValued()
returns Table
as
return (select * from testTbl where id = 1)
Go
update tblValued() set name ='ajay sharma' where id = 1
Go
select * from testTbl
Go
"Functions have only READ-ONLY Database Access"
If DML operations would be allowed in functions then function would be prety similar to stored Procedure.
No, you can not do Insert/Update/Delete.
Functions only work with select statements. And it has only READ-ONLY Database Access.
In addition:
Functions compile every time.
Functions must return a value or result.
Functions only work with input parameters.
Try and catch statements are not used in functions.
CREATE FUNCTION dbo.UdfGetProductsScrapStatus
(
#ScrapComLevel INT
)
RETURNS #ResultTable TABLE
(
ProductName VARCHAR(50), ScrapQty FLOAT, ScrapReasonDef VARCHAR(100), ScrapStatus VARCHAR(50)
) AS BEGIN
INSERT INTO #ResultTable
SELECT PR.Name, SUM([ScrappedQty]), SC.Name, NULL
FROM [Production].[WorkOrder] AS WO
INNER JOIN
Production.Product AS PR
ON Pr.ProductID = WO.ProductID
INNER JOIN Production.ScrapReason AS SC
ON SC.ScrapReasonID = WO.ScrapReasonID
WHERE WO.ScrapReasonID IS NOT NULL
GROUP BY PR.Name, SC.Name
UPDATE #ResultTable
SET ScrapStatus =
CASE WHEN ScrapQty > #ScrapComLevel THEN 'Critical'
ELSE 'Normal'
END
RETURN
END
Functions are not meant to be used that way, if you wish to perform data change you can just create a Stored Proc for that.
if you need to run the delete/insert/update you could also run dynamic statements. i.e.:
declare
#v_dynDelete NVARCHAR(500);
SET #v_dynDelete = 'DELETE some_table;';
EXEC #v_dynDelete
Just another alternative using sp_executesql (tested only in SQL 2016).
As previous posts noticed, atomicity must be handled elsewhere.
CREATE FUNCTION [dbo].[fn_get_service_version_checksum2]
(
#ServiceId INT
)
RETURNS INT
AS
BEGIN
DECLARE #Checksum INT;
SELECT #Checksum = dbo.fn_get_service_version(#ServiceId);
DECLARE #LatestVersion INT = (SELECT MAX(ServiceVersion) FROM [ServiceVersion] WHERE ServiceId = #ServiceId);
-- Check whether the current version already exists and that it's the latest version.
IF EXISTS(SELECT TOP 1 1 FROM [ServiceVersion] WHERE ServiceId = #ServiceId AND [Checksum] = #Checksum AND ServiceVersion = #LatestVersion)
RETURN #LatestVersion;
-- Insert the new version to the table.
EXEC sp_executesql N'
INSERT INTO [ServiceVersion] (ServiceId, ServiceVersion, [Checksum], [Timestamp])
VALUES (#ServiceId, #LatestVersion + 1, #Checksum, GETUTCDATE());',
N'#ServiceId INT = NULL, #LatestVersion INT = NULL, #Checksum INT = NULL',
#ServiceId = #ServiceId,
#LatestVersion = #LatestVersion,
#Checksum = #Checksum
;
RETURN #LatestVersion + 1;
END;

How to detect interface break between stored procedure

I am working on a large project with a lot of stored procedures. I came into the following situation where a developer modified the arguments of a stored procedure which was called by another stored procedure.
Unfortunately, nothing prevents the ALTER PROC to complete.
Is there a way to perform those checks afterwards ?
What would be the guidelines to avoid getting into that kind of problems ?
Here is a sample code to reproduce this behavior :
CREATE PROC Test1 #arg1 int
AS
BEGIN
PRINT CONVERT(varchar(32), #arg1)
END
GO
CREATE PROC Test2 #arg1 int
AS
BEGIN
DECLARE #arg int;
SET #arg = #arg1+1;
EXEC Test1 #arg;
END
GO
EXEC Test2 1;
GO
ALTER PROC Test1 #arg1 int, #arg2 int AS
BEGIN
PRINT CONVERT(varchar(32), #arg1)
PRINT CONVERT(varchar(32), #arg2)
END
GO
EXEC Test2 1;
GO
DROP PROC Test2
DROP PROC Test1
GO
Sql server 2005 has a system view sys.sql_dependencies that tracks dependencies. Unfortunately, it's not all that reliable (For more info, see this answer). Oracle, however, is much better in that regard. So you could switch. There's also a 3rd party vendor, Redgate, who has Sql Dependency Tracker. Never tested it myself but there is a trial version available.
I have the same problem so I implemented my poor man's solution by creating a stored procedure that can search for strings in all the stored procedures and views in the current database. By searching on the name of the changed stored procedure I can (hopefully) find EXEC calls.
I used this on sql server 2000 and 2008 so it probably also works on 2005. (Note : #word1, #word2, etc must all be present but that can easily be changed in the last SELECT if you have different needs.)
CREATE PROCEDURE [dbo].[findWordsInStoredProceduresViews]
#word1 nvarchar(4000) = null,
#word2 nvarchar(4000) = null,
#word3 nvarchar(4000) = null,
#word4 nvarchar(4000) = null,
#word5 nvarchar(4000) = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- create temp table
create table #temp
(
id int identity(1,1),
Proc_id INT,
Proc_Name SYSNAME,
Definition NTEXT
)
-- get the names of the procedures that meet our criteria
INSERT #temp(Proc_id, Proc_Name)
SELECT id, OBJECT_NAME(id)
FROM syscomments
WHERE OBJECTPROPERTY(id, 'IsProcedure') = 1 or
OBJECTPROPERTY(id, 'IsView') = 1
GROUP BY id, OBJECT_NAME(id)
-- initialize the NTEXT column so there is a pointer
UPDATE #temp SET Definition = ''
-- declare local variables
DECLARE
#txtPval binary(16),
#txtPidx INT,
#curText NVARCHAR(4000),
#counterId int,
#maxCounterId int,
#counterIdInner int,
#maxCounterIdInner int
-- set up a double while loop to get the data from syscomments
select #maxCounterId = max(id)
from #temp t
create table #tempInner
(
id int identity(1,1),
curName SYSNAME,
curtext ntext
)
set #counterId = 0
WHILE (#counterId < #maxCounterId)
BEGIN
set #counterId = #counterId + 1
insert into #tempInner(curName, curtext)
SELECT OBJECT_NAME(s.id), text
FROM syscomments s
INNER JOIN #temp t
ON s.id = t.Proc_id
WHERE t.id = #counterid
ORDER BY s.id, colid
select #maxCounterIdInner = max(id)
from #tempInner t
set #counterIdInner = 0
while (#counterIdInner < #maxCounterIdInner)
begin
set #counterIdInner = #counterIdInner + 1
-- get the pointer for the current procedure name / colid
SELECT #txtPval = TEXTPTR(Definition)
FROM #temp
WHERE id = #counterId
-- find out where to append the #temp table's value
SELECT #txtPidx = DATALENGTH(Definition)/2
FROM #temp
WHERE id = #counterId
select #curText = curtext
from #tempInner
where id = #counterIdInner
-- apply the append of the current 8KB chunk
UPDATETEXT #temp.definition #txtPval #txtPidx 0 #curtext
end
truncate table #tempInner
END
-- check our filter
SELECT Proc_Name, Definition
FROM #temp t
WHERE (#word1 is null or definition LIKE '%' + #word1 + '%') AND
(#word2 is null or definition LIKE '%' + #word2 + '%') AND
(#word3 is null or definition LIKE '%' + #word3 + '%') AND
(#word4 is null or definition LIKE '%' + #word4 + '%') AND
(#word5 is null or definition LIKE '%' + #word5 + '%')
ORDER BY Proc_Name
-- clean up
DROP TABLE #temp
DROP TABLE #tempInner
END
You can use sp_refreshsqlmodule to attempt to re-validate SPs (this also updates dependencies), but it won't validate this particular scenario with parameters at the caller level (it will validate things like invalid columns in tables and views).
http://www.mssqltips.com/tip.asp?tip=1294 has a number of techniques, including sp_depends
Dependency information is stored in the SQL Server metadata, including parameter columns/types for each SP and function, but it isn't obvious how to validate all the calls, but it is possible to locate them and inspect them.

Resources