I have db A and db B. At the beginning of a stored procedure I want to back up all rows from B.mytable to B.mytablebackup. The rest of the stored procedure runs against tables on db A (which gathers data and writes it to B.mytable).
So I check to see if B.mytablebackup exists
IF EXISTS(SELECT 1 FROM B.dbo.mytablebackup)
and if it does, the stored procedure does an
INSERT INTO B..mytablebackup SELECT * FROM B..mytable
If it doesn't exist it does a
SELECT * INTO B..mytablebackup from B..mytable
But when I execute the stored procedure I get the error
There is already an object named 'mytablebackup' in the database
I added a Print statement and execution is taking the "does not exist" branch of the IF.
What am I doing wrong?
For SQL Server, you should use system view sys.tables to check if table exists.
IF EXISTS(SELECT 1 FROM B.sys.tables WHERE name = 'mytablebackup')
OBJECT_ID can be used too:
IF OBJECT_ID('B.dbo.mytablebackup') IS NOT NULL
You can directly check from the given DB,SCHEMA and TABLE parameters (For dynamic database, schema and table use)
DECLARE #targetdatabase NVARCHAR(MAX),
#SchemaName NVARCHAR(MAX),
#TableName NVARCHAR(MAX)
DECLARE #TempTableName NVARCHAR(MAX) = QUOTENAME(#targetdatabase) + '.' +
QUOTENAME(#SchemaName) + '.' + QUOTENAME(#TableName)
IF OBJECT_ID(#TempTableName) IS NULL
BEGIN
PRINT #TempTableName
END
Related
I'm checking the validity of existing stored procedures, by obtaining their definition and running the ALTER statement on them.
The problem I have is that any stored procedure which doesn't compile (because a dependency has gone) isn't being flagged as such.
If I try to run the same ALTER command in SSMS I do get the error message.
EDIT: No, I don't....
DECLARE #def nvarchar(MAX)
BEGIN TRY
-- refresh the stored procedure
SELECT #def = REPLACE(definition,'CREATE PROCEDURE ','ALTER PROCEDURE ')
FROM sys.sql_modules
WHERE ... -- selecting/limiting clause
EXEC (#def);
END TRY
BEGIN CATCH
PRINT 'Validation failed : ' + ERROR_MESSAGE()
END CATCH
What do I have to do to trap the non-compile error? Thanks
SQL Server stored procedures use deferred name resolution:
When a stored procedure is created, the statements in the procedure are parsed for syntactical accuracy. If a syntactical error is encountered in the procedure definition, an error is returned and the stored procedure is not created. If the statements are syntactically correct, the text of the stored procedure is stored in the sys.sql_modules catalog view.
When a stored procedure is executed for the first time, the query processor reads the text of the stored procedure from the sys.sql_modules catalog view and checks that the names of the objects used by the procedure are present. This process is called deferred name resolution because table objects referenced by the stored procedure need not exist when the stored procedure is created, but only when it is executed.
So the behavior you observe is intentional. What you need is to find out what procedures depend on your missing tables. For this, see View the Dependencies of a Stored Procedure and the proper answer depends on your SQL Server version. SQL Server 2016 is somehow better at tracking this information and offers better views. Before that the process was notoriously difficult unreliable, read Keeping sysdepends up to date in SQL Server 2008.
Forget this - barking up the wrong tree ;-((
The stored proc will compile OK even if its dependencies have gone.
The editor in SSMS highlights the missing items, but doesn't stop the ALTER statement from working.
This query will identify all stored procs with missing dependencies:
-- table variable to store procedure names
DECLARE #v TABLE (RecID INT IDENTITY(1,1), spname sysname)
-- retrieve the list of stored procedures
INSERT INTO #v(spname)
SELECT
'[' + s.[name] + '].[' + sp.name + ']'
FROM sys.procedures sp
INNER JOIN sys.schemas s ON s.schema_id = sp.schema_id
WHERE is_ms_shipped = 0
AND sp.name like 'Get%'
-- counter variables
DECLARE #cnt INT, #Tot INT
SELECT #cnt = 1
SELECT #Tot = COUNT(*) FROM #v
DECLARE #spname sysname
DECLARE #ref nvarchar(MAX)
-- start the loop
WHILE #Cnt <= #Tot BEGIN
SELECT #spname = spname
FROM #v
WHERE RecID = #Cnt
BEGIN
SELECT #ref = referenced_entity_name
FROM sys.dm_sql_referenced_entities (#spname, 'OBJECT')
WHERE referenced_id IS NULL;
END
SET #Cnt = #cnt + 1
END
SSMS: 2008 R2
We are having our software system updated, which may contain an unknown number of undocumented changes to the way data is entered and stored in our database. We have asked for documentation, but only have schema compares for "physical" changes to the database, not the way the data is treated. They may change in the future, but for now we have to assume not.
In order to check that our stored procedures work as expected after the update, we would like to run a sample of procedures using a sample of parameters before and after the update to compare the actual data results. The stored procedures here all take a single Id as the parameter (they are used to make SSRS reports within the software system)
I have set some things up, but I am having problems with my approach and would welcome any suggestions about either a better way to do things, or how to fix my approach. The problem is that an error is returned whenever a called stored procedure uses a temporary table. Here is what I have done:
Made a script to get a random sample of Ids for paramaters (only one table used at the moment - that's fine).
ALTER PROC [dbo].[UpdateValidation_GET_RandomIdSample](#TestSizePercent DECIMAL(6,3))
AS
-- This table is already created and will persist both sides of the update
--CREATE TABLE Live_Companion.dbo.UpdateValidationIds
--( Id INT IDENTITY(1,1)NOT NULL PRIMARY KEY CLUSTERED
-- ,MyTableId NT NULL)
IF #TestSizePercent > 100 RAISERROR('Do you even percent, bro?',16,1)
DECLARE #SQL VARCHAR(255)
TRUNCATE TABLE UpdateValidationIds
SET #SQL =
'INSERT dbo.UpdateValidationIds(Id)
SELECT TOP ' + CONVERT(VARCHAR(10),#TestSizePercent) + ' PERCENT ID FROM Live.dbo.MyTable ORDER BY NEWID()'
EXEC (#SQL)
Made a second script to run a stored procedure for each Id in the table:
ALTER PROC [dbo].[UpdateValidation_GET_ProcedureResultsManyTimes](#Procedure_Name VARCHAR(255))
AS
--DECLARE #Procedure_Name VARCHAR(255) = 'Live_Companion.dbo.MyProc'
DECLARE #ID INT
DECLARE #GET_ID CURSOR
DECLARE #SQL VARCHAR(MAX) = ''
DECLARE #MyTableId INT
DECLARE #FirstRun BIT = 1
SET #GET_ID = CURSOR FOR
SELECT Id FROM Live_Companion.dbo.UpdateValidationIds
WHERE MyTableId IS NOT NULL
OPEN #GET_ID
FETCH NEXT FROM #GET_ID INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #MyTableId = MyTableId FROM Live_Companion.dbo.UpdateValidationIds
WHERE Id = #ID
IF #FirstRun = 1
BEGIN
SET #SQL = 'SELECT * INTO #ProcedureOutput FROM OPENROWSET(''SQLNCLI'',''Server=SQL1;Trusted_Connection=yes;'',''EXEC ' + #Procedure_Name + ' ' + CONVERT(VARCHAR(50),#MyTableId) + ''');'
SET #FirstRun = 0
END
ELSE
BEGIN
SET #SQL = #SQL + '
INSERT #ProcedureOutput SELECT * FROM OPENROWSET(''SQLNCLI'',''Server=SQL1;Trusted_Connection=yes;'',''EXEC ' + #Procedure_Name + ' ' + CONVERT(VARCHAR(50),#MyTableId) + ''');'
END
FETCH NEXT FROM #GET_ID INTO #ID
END
SET #SQL = #SQL + '
SELECT * FROM #ProcedureOutput
DROP TABLE #ProcedureOutput'
EXEC (#SQL)
CLOSE #GET_ID
DEALLOCATE #GET_ID
So now I should be able to execute the second procedure for various stored procedures and output the results to file over a range of Ids, then repeat using the saved (initially random) Ids again after the update and compare the results.
The trouble is, it fails when any of the called procedures use a temporary table:
EDIT:
Error Message returned:
Cannot process the object "EXEC Live_Companion.dbo.MyProc 12345". The
OLE DB provider "SQLNCLI10" for linked server "(null)" indicates that
either the object has no columns or the current user does not have
permissions on that object.
Any suggestions or ideas for how to proceed?
I have to create a stored procedure where I will pass tableName, columnName, id as parameters. The task is to select records from the passed table where columnName has passed id. If record is found update records with some fixed data. Also implement Transaction so that we can rollback in case of any error.
There are hundreds of table in database and each table has different schema that is why I have to pass columnName.
Don't know what is the best approach for this. I am trying select records into a temp table so that I can manipulate it as per requirement but its not working.
I am using this code:
ALTER PROCEDURE [dbo].[GetRecordsFromTable]
#tblName nvarchar(128),
#keyCol varchar(100),
#key int = 0
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
--DROP TABLE #TempTable;
DECLARE #sqlQuery nvarchar(4000);
SET #sqlQuery = 'SELECT * FROM ' + #tblName + ' WHERE ' + #keyCol + ' = 2';
PRINT #sqlQuery;
INSERT INTO #TempTable
EXEC sp_executesql #sqlQuery,
N'#keyCol varchar(100), #key int', #keyCol, #key;
SELECT * FROM #TempTable;
END TRY
BEGIN CATCH
EXECUTE [dbo].[uspPrintError];
END CATCH;
END
I get an error
Invalid object name '#TempTable'
Also not sure if this is the best approach to get data and then update it.
If you absolutely must make that work then I think you'll have to use a global temp table. You'll need to see if it exists before running your dynamic sql and clean up. With a fixed table name you'll run into problems with other connections. Inside the dynamic sql you'll add select * into ##temptable from .... Actually I'm not even sure why you want the temp table in the first place. Can't the dynamic sql just return the results?
On the surface it seems like a solid idea to have one generic procedure for returning data with a couple of parameters to drive it but, without a lot of explanation, it's just not the way database are designed to work.
You should create the temp table.
IF OBJECT_ID('tempdb..##TempTable') IS NOT NULL
DROP TABLE ##TempTable
CREATE TABLE ##TempTable()
When I run the following code, I get an "invalid object name" error, any idea why?
I need to create a dynamically named temp table to be used in a stored procedure.
DECLARE #SQL NVARCHAR(MAX)
DECLARE #SessionID NVARCHAR(50)
SET #SessionID = 'tmp5l7g9q3l1h1n5s4k9k7e'
;
SET
#SQL = N' CREATE TABLE #' + #SessionID + ' ' +
N' (' +
N' CustomerNo NVARCHAR(5), ' +
N' Product NVARCHAR(3), ' +
N' Gross DECIMAL(18,8) ' +
N' )'
;
EXECUTE sp_executesql #SQL
;
SET
#SQL = N' SELECT * FROM #' + #SessionID
;
EXECUTE sp_executesql #SQL
Thanks!
WHY MESS WITH THE NAMES? Let SQL Server will manage this for you:
Temporary Tables in SQL Server
from the above link:
If the same routine is executed simultaneously by several processes,
the Database Engine needs to be able to distinguish between the
identically-named local temporary tables created by the different
processes. It does this by adding a numeric string to each local
temporary table name left-padded by underscore characters. Although
you specify the short name such as #MyTempTable, what is actually
stored in TempDB is made up of the table name specified in the CREATE
TABLE statement and the suffix. Because of this suffix, local
temporary table names must be 116 characters or less.
If you’re interested in seeing what is going on, you can view the
tables in TempDB just the same way you would any other table. You can
even use sp_help work on temporary tables only if you invoke them from
TempDB.
USE TempDB
go
execute sp_Help #mytemp
or you can find them in the system views of TempDB without swithching
databases.
SELECT name, create_date FROM TempDB.sys.tables WHERE name LIKE '#%'
You are doing it wrong!
Try:
exec(#SQL)
instead of:
EXECUTE sp_executesql #SQL
To use sp_executesql the variable must be inside #SessionID the quotes and it must be provided has input parameter. Check this for a full example!
You've to be aware that Dynamic SQL is a good port for SQL injections!
This syntax works
CREATE TABLE #SessionID (CustomerNo NVARCHAR(5), Product NVARCHAR(3), Gross DECIMAL(18,8));
Select COUNT(*) from #SessionID;
Drop Table #SessionID;
In SQL Server 2008, is there a way to access an object based on the OBJECT_ID?
[Edit] The example described below was solved using Andrew's suggestion in the comment, but I'm still curious about the general case. Can an object itself be retrieved using OBJECT_ID, or can it only be accessed indirectly by using the object name via sp_executesql?
My specific case is a stored procedure that uses several temporary tables. At the end of the procedure I want to dump the data from the temporary tables into actual tables for analysis (only if a debug switch is enabled).
The code for dumping the data is similar to this:
IF OBJECT_ID('tempdb..#MyTempTable', 'U') IS NOT NULL
BEGIN
IF OBJECT_ID('Debug_MyTempTable', 'U') IS NOT NULL
DROP TABLE Debug_MyTempTable
SELECT * INTO Debug_MyTempTable FROM #MyTempTable
END
This code block is repeated for each temporary table, so I would prefer to put it in a procedure and call it with a table name:
EXEC [dbo].[CreateDebugTable]
#tableName = 'MyTempTable'
I imagine the procedure would look something like:
CREATE PROCEDURE [dbo].[CreateDebugTable]
#tableName VARCHAR(50)
AS
BEGIN
IF OBJECT_ID('tempdb..#' + #tableName, 'U') IS NOT NULL
BEGIN
IF OBJECT_ID('dbo.Debug_' + #tableName, 'U') IS NOT NULL
DROP TABLE <Debug_TempTable>
SELECT * INTO <Debug_TempTable> FROM <#TempTable>
END
END
The procedure depends on being able to translate the OBJECT_ID of DebugTempTable and #TempTable into the actual tables (shown with <> in the code above). Is this possible?
[Edit]
This is the altered procedure using sp_executesql instead of explicit tables.
CREATE PROCEDURE [dbo].[CreateDebugTable]
#tableName VARCHAR(50)
AS
BEGIN
DECLARE #tmpTable VARCHAR(50) = '#' + #tableName
DECLARE #dboTable VARCHAR(50) = 'Debug_' + #tableName
DECLARE #sql NVARCHAR(100)
IF OBJECT_ID('tempdb..' + #tmpTable, 'U') IS NOT NULL
BEGIN
IF OBJECT_ID('dbo.' + #dboTable, 'U') IS NOT NULL
BEGIN
SET #sql = 'DROP TABLE ' + #dboTable
EXECUTE sp_executesql #sql
END
SET #sql = 'SELECT * INTO ' + #dboTable + ' FROM ' + #tmpTable
EXECUTE sp_executesql #sql
END
END
The object_id is just used as a key in the various metadata views. There is no TSQL syntax to SELECT from (or otherwise manipulate) objects based on their object_id.
If you have an object_id then in general you could use
SELECT QUOTENAME(OBJECT_SCHEMA_NAME(#object_id[,database_id])) +
'.' +
QUOTENAME(OBJECT_NAME(#object_id[,database_id]) )
To get the 2 part name of the object but for #temp tables this returns the long internal name rather than the short one that you can actually use in queries.