Retrieve column definition for stored procedure result set - sql-server

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

Related

Create table on the fly using select into

I am trying to use dynamic SQL to fill a temp table with data from one of several servers, depending on a declared variable. The source data may have more columns added in the future, so I'd like to be able to create the destination temp table based on what columns currently exist, without having to explicitly define it.
I tried creating an empty table with the appropriate columns using:
Select top 1 * into #tempTable from MyTable
Delete from #tempTable
Or:
Select * into #tempTable from MyTable where 1 = 0
Both worked to create an empty table, but when I then try to insert into it:
declare #sql varchar(max) = 'Select * from '
+ case when #server = '1' then 'Server1.' else 'Server2.' end
+ 'database.dbo.MyTable'
Insert into #tempTable
exec(#sql)
I get this error:
Msg 213, Level 16, State 7, Line 1
Column name or number of supplied values does not match table definition.
exec(#sql) works fine on its own. I get this error even when I use the same table, on the same server, for both steps. Is this possible to fix, or do I have to go back to explicitly defining the table with create table?
How about using global temp table. there is some disadvantage of using global temp table because it can access from multiple users and databases. ref http://sqlmag.com/t-sql/temporary-tables-local-vs-global
DECLARE #sql nvarchar(max) = 'SELECT * INTO ##tempTable FROM '
+ case when #server = '1' THEN 'Server1.' ELSE 'Server2.' END
+ 'database.dbo.MyTable'
EXECUTE sp_executesql (#sql)
SELECT * FROM ##tempTable
(Thanks to helpful commenter #XQbert)
Replacing the ID column (Int, Identity) in the temp table with a column that was just an int causes
Insert into #tempTable
exec(#sql)
to function as intended.
Both that syntax and
declare #sql varchar(max) = 'Insert into #tempTable Select * from '
+ case when #server = '1' then 'Server1.' else 'Server2.' end
+ 'database.dbo.MyTable'
exec(#sql)
worked, but making insert part of the dynamic sql produced much more helpful error messages for troubleshooting.

Tempdb Full When Querying Distinct Count Of All Tables

ORIGINAL PROBLEM
I have created a custom script to retrieve data from a remote SQL server into our local copy in our office. I had some issues with the script where selected tables had some data inserted twice, thus creating duplicates. I know that for all the tables in all databases there should be no duplicates.
This issue has made me paranoid that other tables may have had this problem historically, and therefore I'd like to verify this.
SOLUTION
I have created a SQL script to insert the count and distinct count of all columns into a table for all the databases on our server (excluding the 4 system databases):
DECLARE #TableFullName AS NVARCHAR(MAX)
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #TableHasDuplicates AS BIT
DECLARE #TempTableRowCount AS INT
DECLARE #ResultsTable TABLE ([CompleteTableName] NVARCHAR(200), [CountAll] INT, [CountDistinct] INT)
DECLARE #CountAll INT
DECLARE #CountDistinct INT
SET NOCOUNT ON
DECLARE #AllTables TABLE ([CompleteTableName] NVARCHAR(200))
INSERT INTO #AllTables ([CompleteTableName])
EXEC sp_msforeachdb 'SELECT ''['' + [TABLE_CATALOG] + ''].['' + [TABLE_SCHEMA] + ''].['' + [TABLE_NAME] + '']'' FROM [?].INFORMATION_SCHEMA.TABLES'
SET NOCOUNT OFF;
DECLARE [table_cursor] CURSOR FOR
(SELECT *
FROM #AllTables
WHERE [CompleteTableName] NOT LIKE '%master%' AND [CompleteTableName] NOT LIKE '%msdb%' AND [CompleteTableName] NOT LIKE '%tempdb%' AND [CompleteTableName] NOT LIKE '%model%');
OPEN [table_cursor]
PRINT N'There were ' + CAST(#CountAll AS NVARCHAR(10)) + ' tables with potential duplicate data'
FETCH NEXT FROM [table_cursor]
INTO #TableFullName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLQuery = 'SELECT #CntAll = COUNT(*) FROM ' + #TableFullName + ' SELECT #CntDistinct = COUNT(*) FROM (SELECT DISTINCT * FROM ' + #TableFullName + ') AS [sq] IF #CntAll > #CntDistinct SELECT #BitResult=1 ELSE SELECT #BitResult=0';
EXEC sp_executesql #SQLQuery, N'#BitResult BIT OUTPUT, #CntAll INT OUTPUT, #CntDistinct INT OUTPUT', #BitResult = #TableHasDuplicates OUTPUT, #CntAll = #CountAll OUTPUT, #CntDistinct = #CountDistinct OUTPUT;
IF #TableHasDuplicates = 1
BEGIN
INSERT INTO #ResultsTable ([CompleteTableName], [CountAll], [CountDistinct])
SELECT #TableFullName, #CountAll, #CountDistinct
END;
FETCH NEXT FROM [table_cursor]
INTO #TableFullName
END
CLOSE [table_cursor];
DEALLOCATE [table_cursor];
SELECT *
FROM #ResultsTable
An overview of how it works is the table variable #AllTables uses sp_msforeachdb with INFORMATION_SCHEMA.TABLES to list all the tables in all databases (there are 16537 tables). A table cursor is used to store all non-system entries and then I use dynamic SQL to undertake a count and distinct count which is stored in another table variable #ResultsTable.
THE PROBLEM WITH THIS SOLUTION
When I run this query, it will run for circa 3 minutes then throw an error saying that the tempdb PRIMARY filegroup is full:
I am my own DBA, and I used Brent Ozar's guide to setting up my SQL server instance, and my tempdb is set up with 8 x 3GB mdf/ndf files (the server has 8 cores):
These files show as having 23997MB available under 'General' properties.
MY QUESTIONS
If I have circa 24GB of tempdb free space, why is this relatively simple query running out of tempdb space?
Is there a better/more efficiency way of getting a count and distinct count of all tables in all databases?
You should always consider contention before adding TempDb file. Adding 7 additional TempDb file won't really help.
If I have circa 24GB of tempdb free space, why is this relatively
simple query running out of tempdb space?
No, it should not. But are you sure that you aren't dealing with large amount of data or you don't have other process running on SQL? Cursors, Temp tables and even table variables use TempDb extensively. Check which object is consuming more TempDb space:
SELECT
SUM (user_object_reserved_page_count)*8 as usr_obj_kb,
SUM (internal_object_reserved_page_count)*8 as internal_obj_kb,
SUM (version_store_reserved_page_count)*8 as version_store_kb,
SUM (unallocated_extent_page_count)*8 as freespace_kb,
SUM (mixed_extent_page_count)*8 as mixedextent_kb
FROM sys.dm_db_file_space_usage
So, if your user and internal objects are more then it clearly means that you have low TempDb space because of cursors and SQL Server internal usage (Ex: intermediate tables, Hash joins, Hash aggregation etc)
Is there a better/more efficiency way of getting a count and distinct
count of all tables in all databases?
You can use below code to get the count of all tables in all databases
DECLARE #Stats TABLE (DBNAME VARCHAR(40), NAME varchar(200), Rows INT)
INSERT INTO #Stats
EXECUTE sp_MSForEachDB
'USE ?; SELECT DB_NAME()AS DBName,
sysobjects.Name
, sysindexes.Rows
FROM
sysobjects
INNER JOIN sysindexes
ON sysobjects.id = sysindexes.id
WHERE
type = ''U''
AND sysindexes.IndId < 2'
SELECT * FROM #Stats
I have written an article on TempDb recommendation; I would suggest you to read that to understand objects which can affect TempDb and how to solve common problems of it. Ideally, your total TempDb size should be calculated based on observation which in your case > 24 GB.
** Edit 1**
If you are unsure about stats update then use below query to get count of all tables
Note: Replace databases for which you don't want stats
DECLARE #ServerStats TABLE (DatabaseName varchar(200), TableName varchar(200), RowsCount INT)
INSERT INTO #ServerStats
exec sp_msforeachdb #command1='
use #;
if ''#'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'',''ReportServer'')
begin
print ''#''
exec sp_MSforeachtable #command1=''
SELECT ''''#'''' AS DATABASENAME, ''''?'''' AS TABLENAME, COUNT(*) FROM ? ;
''
end
', #replacechar = '#'
SELECT * FROM #ServerStats
similarly you can take distinct in all tables for all databases with below query
DECLARE #ServerStatsDistinct TABLE (DatabaseName varchar(200), TableName varchar(200), RowsCount INT)
INSERT INTO #ServerStatsDistinct
exec sp_msforeachdb #command1='
use #;
if ''#'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'',''ReportServer'')
begin
print ''#''
exec sp_MSforeachtable #command1=''
SELECT ''''#'''' AS DATABASENAME, ''''?'''' AS TABLENAME, COUNT(*) FROM (
SELECT DISTINCT *
FROM ?
) a ;
''
end
', #replacechar = '#'
SELECT * FROM #ServerStatsDistinct

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

Store Procedure is working perfectly fine when executed from front end but is not working when I execute it in sql server

I have created a stored procedure which works perfectly fine when I execute it from from end i.e.C#.net or when I copy and past the query of stored procedure in separate SQL Query window but it neither give any result nor any error when I execute it in SQL server using EXEC sp_Name. I can not understand what is wrong in my code.
Below is my stored procedure
ALTER PROCEDURE sp_tbl_REQUEST_SelectAllByFilter_WithPagging
#PageIndex INT=NULL,
#PageRecord INT=NULL,
#SortExpression NVARCHAR (200)='int_Request_ID',
#SortDirection NVARCHAR (10)='ASC',
#int_Requester_ID INT=NULL,
#intProjectID INT=NULL
AS
BEGIN
SET NOCOUNT ON
DECLARE #StartRowIndex INT
SET #StartRowIndex=((#PageIndex-1)*#PageRecord)+1;
DECLARE #WhrClause VARCHAR(MAX)
SET #WhrClause= 'WHERE tr.int_Requester_ID = '+CONVERT(VARCHAR(MAX), #int_Requester_ID)
IF(#intProjectID>0)
BEGIN
SET #WhrClause=#WhrClause+' AND tr.int_Project_ID='+CONVERT(VARCHAR(MAX),#intProjectID)
END
DECLARE #SelectClause VARCHAR(MAX)
SET #SelectClause=';With AllRecords AS(SELECT Row_Number() OVER(ORDER BY '+CONVERT (VARCHAR (MAX), #SortExpression)+' '+CONVERT (VARCHAR (MAX), #SortDirection)+')AS ''RowNumber'',* FROM(SELECT tr.[int_Request_ID],
tr.[int_User_ID],
tr.[int_Project_ID],
tr.[str_Request_Type],
tr.[int_Account_ID],
tr.[int_Requester_ID],
tr.[int_User_Head_ID],
tr.[dt_Request_Date],
tr.[bln_IsApproved],
tr.[dt_Approval_Date],
tr.[str_Reject_Reason],
tr.[int_USER_ROLE],
tr.[int_AllocationType_ID],
tr.[int_NoofHourInMinute],
xyz.dbo.GetHourByMin(ISNULL(int_NoofHourInMinute,0)) as ''str_NoofHour'',
tr.[dt_StartDateToWork],
tr.[dt_EndDateToWork],
tr.[isAllowToAddTask],
tr.[isAllowToDeleteTask],
tr.[isAllowToAddCR],
tp.str_Project_Name,
tu.str_FullName AS str_User_Name,
tu1.str_FullName AS UserHeader,
tbl_Project_AllocationType.str_AllocationType,
tu2.str_FullName AS UserRequester,
tu2.str_EMAIL_ADDRESS as RequesterEmail
FROM [tbl_Requests] tr
INNER JOIN tbl_PROJECT tp ON tp.int_Project_ID = tr.int_Project_ID
INNER JOIN tbl_USER tu ON tu.int_USER_ID=tr.int_User_ID
LEFT JOIN tbl_USER tu1 ON tu1.int_USER_ID=tr.int_User_Head_ID
INNER JOIN tbl_USER tu2 ON tu2.int_USER_ID=tr.int_Requester_ID
inner join tbl_Project_AllocationType on tbl_Project_AllocationType.int_AllocationType_ID=tr.int_AllocationType_ID '+#WhrClause+'
)As Tmp)
SELECT * FROM
AllRecords WHERE RowNumber BETWEEN ' + CONVERT(VARCHAR(MAX), #StartRowIndex) + ' AND ' + CONVERT(VARCHAR(MAX), (#StartRowIndex + #PageRecord - 1)) + '
SELECT COUNT(TempTbl.int_Request_ID)As ''ReturnRecords'','+CONVERT(VARCHAR(MAX),#PageIndex)+'''PageIndex''
FROM (SELECT tr.[int_Request_ID]
FROM [tbl_Requests] tr '+#WhrClause+')as TempTbl;'
PRINT(#SelectClause)
DECLARE #SQL NVARCHAR(MAX)
SET #SQL=CONVERT(NVARCHAR(MAX),#SelectClause)
EXEC sp_executesql #SQL
END
I am executing it like this.
EXEC sp_tbl_REQUEST_SelectAllByFilter_WithPagging 1,20,NULL,NULL,74,591
You're passing NULL for #SortExpression and #SortDirection. NULL means that the defaults don't apply - they're null instead.
So the whole concatenation into #SelectClause becomes NULL (because concatenating strings and NULLs produces NULLs)
So nothing is executed.
Try:
EXEC sp_tbl_REQUEST_SelectAllByFilter_WithPagging
1,20,#int_Requester_ID = 74,#intProjectID=591
Incidentally, you should avoid using sp_ as a prefix for stored procedures. The sp_ prefix is reserved by MS for system stored procedures, and SQL Server will prefer a system stored procedure from master vs your own procedure, if there's a name clash.
(I'd generally recommend against using any prefixes in SQL Server, but that's more of a matter for debate, rather than a strong rule)
Hello You don't need to pass the parameter null
so you can execute store procedure this way
EXEC sp_tbl_REQUEST_SelectAllByFilter_WithPagging 1,20,#int_Requester_ID=74,#intProjectID=591
Try this
Regards
Amit Vyas

Check if table exists in 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

Resources