How to pass database name into stored procedure? - sql

How to pass database name into stored procedure? I tried something like
create procedure dbo.Test
#databaseName varchar(100)
as
select * from #databasename.Person.Address
go
I would like to use it like this
execute dbo.Test #databaseName = 'AdventureWorks'

This is not possible in the manner you are describing.
You can do this with dynamic SQL, but that brings its own set of problems.

Sounds like you are trying to move information which should be part of connection string into stored procedure logic. If you expect that Person table will be in different database this database name should be fixed in deployment - for example by parametrized creation script and sqlcmd.

Related

Loop Through All SSMS Databases without Recreating Stored Procedure

Background Information:
In Python, I might write something like this if I want to apply the same logic to different values in a list.
database_list = ["db_1", "db_2", "db_3"]
for x in range(0,len(database_list),1):
print("the database name is " + database_list[x])
What I am trying to do:
What I am trying to do in SSMS, is pull a list of DB objects for each database. I created a stored procedure to pull exactly what I want, but I have to run it against each database, so 10 databases mean running it 10 times.
My goal is to do this with a T-SQL query instead of Python.
I tried doing something like this:
exec sp_MSforeachdb 'USE ?; EXEC [dbo].[my_stored_procedure]';
The problem with this is, [dbo].[my_stored_procedure] has to exist in every database I want to do this in.
How can I create the stored procedure in 1 database, but execute it for all databases or a list of databases that I choose?
I know what you are trying to do and if it's what I think (you seem reluctant to actually say!) you can do the following:
In the master database, create your procedure. Normally you wouldn't do this, but in this case you must prefix it sp_
use master
go
create procedure sp_testproc as
select top 10 * from sys.tables
go
Now if you run this, it will return tables from the master database.
If you switch context to another database and exec master.dbo.sp_testproc, it will still return tables from the master database.
In master, run
sys.sp_MS_marksystemobject sp_testproc
Now switch context to a different database and exec master.dbo.sp_testproc
It will return tables from the database you are using.
Try creating your sproc in master and naming it with an sp_ prefix:
USE master
GO
CREATE PROCEDURE sp_sproc_name
AS
BEGIN
...
END
GO
-- You *may* need to mark it as a system object
EXEC sys.sp_MS_marksystemobject sp_sprocname
See: https://nickstips.wordpress.com/2010/10/18/sql-making-a-stored-procedure-available-to-all-databases/
It should then be available in all dbs
Create the stored procedure in the Master database with the sp_ prefix, and use dynamic SQL in the stored procedure so it resolves object names relative to the current database, rather than the database which contains the stored procedure.
EG
use master
go
CREATE OR ALTER PROCEDURE [dbo].[sp_getobjects]
AS
exec ('
select *
from [sys].[objects]
where is_ms_shipped = 0
order by type, name
')
go
use AdventureWorks2017
exec sp_getobjects
#LunchBox - it's your single stored procedure (that you create in one database) that is actually going to need to contain the "exec sp_MSforeach ...." command, and instead of the command to be executed being "EXEC ", it will need to be the actual SQL that you were going to put into the stored proc.
Eg. (inside your single stored procedure)
EXEC sp_MSforeachdb 'USE ?; SELECT * FROM <table>; UPDATE <another table> SET ...';
Think of the stored procedure (that you put into one database) as being no different than your Python code file - if you had actually wanted to achieve the same thing in Python, you would have either needed to create the stored proc in each database, or build the SQL statement string in Python and execute it against each database.
I understand what you thought you might be able to achieve with SQL, but stored procedures really don't work the way you were expecting. Even when you're in the context of a different database, but you run EXEC <different_db>.stored_proc, that stored proc ends up running in the context of the database in which it exists (not your context database).
Now, the only one issue you may come up against is that the standard sp_MSforeachdb stored proc has a limit of 2000 characters for the command that can be executed (although, it does have multiple "command" parameters, this may not be practical if you were planning on running a very large code block, perhaps with variables that carry all the way through). If this is something that might impact what you're intending to do, you could do a search online for "sp_MSforeachdb alternatives" - there seem to be a handful that people have created where the command parameter can contain a larger string.

Update Stored procedure (T-SQL ) modification automatically?

I have two identical stored procedures with dynamic query.
Let's say
Stored procedure A
Stored procedure B
Both are in different databases. But they have the same code (Not Complete Identical.4-5 lines differ).
Is there a way to update any modification done in stored procedure A to stored procedure B automatically?
Otherwise I always need to copy and paste changes manually. It is an error-prone activity. Can anyone help me on this ?
You could do something like that:
In database A:
design your stored procedure in a way, that you have a parameter for
the database in which you want to do the work
In database B:
Create a synonym for the procedure in database A
Example:
--create procedure in database A
create procedure dbo.StoredProc
(
#dbname --or dbid if you want
)
as
begin
--create your sql command here, using dynamic sql maybe
declare #sqlcmd NVARCHAR(MAX)=N''
set #sqlcmd = 'SELECT * FROM ' + #dbname + '.dbo.AnyTable'
exec sp_executesql #sqlcmd
end
--create a synonym for this procedure in database b:
create synonym dbo.StoredProc FOR databaseA.dbo.StoredProc
--then you can call your procedure in Database A and B like this:
declare #dbname NVARCHAR(100) = DB_NAME()
exec dbo.StoredProc #dbname
so you have to maintain your code only once, and in database b you only have kind of a "link" to this procedure.
hope this helps :)
This is basically what SSDT was designed for, the idea is that you write your T-SQL and schema as CREATE statements, you build a "dacpac" and then you use sqlpackage.exe to deploy the dacpac to whatever database you want.
Doing it this way you have an overhead of the SSDT project but it fixes exactly your main problem with the existing method "It is an error-prone activity."
My blog post shows how to get an existing database into SSDT (in this case adventureworks but replace adventureworks with your database):
https://the.agilesql.club/Blog/Ed-Elliott/AdventureWorksCI-Step2-MDF-To-Dot-Sql
Ed

Selecting data from a different schema within a stored procedure

Consider this:
CREATE PROCEDURE [dbo].[setIdentifier](#oldIdentifierName as varchar(50), #newIdentifierName as varchar(50))
AS
BEGIN
DECLARE #old_id as int;
DECLARE #new_id as int;
SET #old_id = (SELECT value FROM Configuration WHERE id = #oldIdentifierName);
SET #new_id = (SELECT value FROM Configuration WHERE id = #newIdentifierName);
IF #old_id IS NOT NULL AND #new_id IS NOT NULL
BEGIN
UPDATE Customer
SET type = #new_id
WHERE type = #old_id;
END;
END
[...]
EXECUTE dbo.setIdentifier '1', '2';
What this does is create a stored procedure that accepts two parameters which it then uses to update a Customer table.
The problem is that the entire script above runs within a schema other than "dbo". Let's just assume the schema is "company1". And when the stored procedure is called, I get an error from the SELECT statement, which says that the Configuration table cannot be found. I'm guessing this is because MS SQL by default looks for tables within the same schema as the location of the stored procedure, and not within the calling context.
My question is this:
Is there some option or parameter or switch of some kind that will
tell MS SQL to look for tables in the "caller's default schema" and
not within the schema that procedure itself is stored in?
If not,
what would you recommend? I don't really want to prefix the tables
with the schema name, because it would be kind of unflexible to do
that. So I'm thinking about using dynamic sql (and the schema_name()
function which returns the correct value even within the procedure),
but I am just not experienced enough with MS SQL to construct the
proper syntax.
It would be a tad more efficient to explicitly specify the schema name. And generally speaking, schema's are mainly used to divide a database into logical area's. I would not anticipate on tables schema-hopping often.
Regarding your question, you might want to have a look at the 'execute as' documentation on msdn, since it allows to explicitly control your execution context.
I ended up passing the schema name to my script as a property on the command line for the "sqlcmd" command. Like this:
C:/> sqlcmd -vSCHEMANAME=myschema -imysqlfile
In the SQL script I can then access this variable like this:
SELECT * from $(SCHEMANAME).myTable WHERE.... etc
Not quite as flexible as dynamic sql, but "good enough" as it were.
Thanks all for taking time to respond.

Stored procedure scope: using sp from another database

I hava a stored procedure named PROC_fetchTableInfo (simple select from sys.tables) in a database named
Cust
I would like use this procedure in a another database named
Dept
I tried to execute this sp in this database using command
EXECUTE Cust.dbo.PROC_fetchTableInfo
but as a result I get tables from the database Cust. How to get this procedure to work in database Dept?
A stored procedure is tighly bound to the objects in its code. If Cust.dbo.PROC_fetchTable references a table T, that is stricly the table T in schema dbo in database Cust. You can invoke the procedure from any other place, it will always refer to this table.
If you need to run the same procedure in another table on another database then the best solution, by far, is to have a new procedure: Dept.dbo.PROC_fetxTableInfo. This is better than the alternative of using Dynamic-SQL. While this seems counteruintuitive from a DRY and code reuse perspective, T-SQL is a data access language is not a programming language, leave your C/C# mind set at the door when you enter the database. Just have another procedure in the Dept database.
Your procedure, Cust.dbo.PROC_fetchTableInfo looks at data in the Cust database. Sounds like you need a copy of it in the Dept database.
Alternately (and quite unsatisfactory to me) you can add a parameter that controls which database to query by building the query in the sproc dynamically.
Perhaps you could create it in some "Common" database and call it like EXEC Common..PROC_fetchTableInfo #databaseName='Dept'
This is possible (if you are trying to write some utility function that you can use across all databases). You can create it in the master database, give it a sp_ prefix and mark it as a system object.
use master
go
CREATE PROCEDURE dbo.sp_sys_tables_select
AS
SELECT * FROM sys.tables
GO
EXEC sys.sp_MS_marksystemobject 'dbo.sp_sys_tables_select'
GO
EXEC tempdb.dbo.sp_sys_tables_select
EXEC msdb.dbo.sp_sys_tables_select

SQL - How to insert results of Stored_Proc into a new table without specifying columns of new table?

Using SQL Server 2005, I'd like to run a stored procedure and insert all of the results into a new table.
I'd like the new table to have its columns automatically configured based upon the data returned by the stored procedure.
I am familiar with using the SELECT ... INTO syntax:
SELECT * INTO newtable FROM oldtable
Is this possible?
Edit for clarification: I'm hoping to accomplish something like:
Select * INTO newtable FROM exec My_SP
The only way to do this is w/ OPENROWSET against the local server:
SELECT * INTO #temp
FROM OPENROWSET (
'SQLOLEDB'
, 'Server=(local);TRUSTED_CONNECTION=YES;'
, 'SET FMTONLY OFF EXEC database.schema.procname'
) a
But this is kind of a last-ditch-gotta-do-it-damn-the-consequences kind of method. It requires elevated permissions, won't work for all procedures, and is generally inefficient.
More info and some alternatives here: http://www.sommarskog.se/share_data.html
This seems like a horrible design. You're really going to create a new table to store the results of a stored procedure, every time the stored procedure is called? And you really can't create the table in advance because you have absolutely no idea what kind of output the stored procedure has? What if the stored procedure returns multiple resultsets? What if it has side effects?
Okay, well, if that's what you really want to do...
One way to accomplish this is to use your local server as a linked server and utilize OPENQUERY. First you need to make sure your local server is configured for data access:
EXEC sp_serveroption 'local server name', 'DATA ACCESS', true;
Then you can do something like this:
SELECT * INTO dbo.newtable
FROM OPENQUERY('local server name', 'EXEC yourdb.dbo.yourproc;');
PS How are you going to write code that is going to perform SELECT INTO into a new table name every time (because you can only do SELECT INTO once)? Dynamic SQL? What happens if two users run this code at the same time? Does one of them win, and the other one just gets an error message?
A variation of the same is
create table somename
select * from wherever;