This is more a hypothetical question, but suppose if someone makes a table change within SQL Server that breaks a Stored Procedure - or a large number of Stored Procedures, is there a way to determine what is broken?
Suppose I have a stored procedure which returns some user data
SELECT user.Id,
user.FirstName,
user.LastName
FROM Users
Then a developer makes a change to the table and changes LastName column name to Surname. But he forgets to change the related stored procedures.
I can use SQL Server to see the dependencies of a stored procedure, but I want to know which stored procedures are just broken.
Or in my case, if I have stored procedures that reference 3rd Party tables and the 3rd Party totally revamps their tables.
Is there any way to check?
You can use schema binding on views, functions, and in versions 14 and up stored procedures as well. This would be a proactive way to prevent a developer from making a change that would break a view, function, or procedure.
Otherwise you'll have to use a script to check. I think this question has some information that could help you:
Syntax check all stored procedures?
For procedures, you can use this cursor:
Declare list_cursor Cursor
For
Select code = 'sp_refreshsqlmodule '''+OBJECT_SCHEMA_NAME(object_id)+'.'+OBJECT_NAME(object_id)+'''' From sys.procedures
Declare #sql nvarchar(max)
Open list_cursor
FETCH NEXT FROM list_cursor INTO #sql
While ##FETCH_STATUS = 0
Begin
Begin Try
Exec sp_executesql #sql
End Try
Begin Catch
print #sql
print ' '+ERROR_MESSAGE()
End Catch
FETCH NEXT FROM list_cursor INTO #sql
END
CLOSE list_cursor
DEALLOCATE list_cursor
Related
I need to pass linked server name as variable to stored procedure right now after testing and research they all suggest to using dynamic sql and open query which I am using now. however I am not comfortable using it(sql injection) plus I need to call other user defined function to the query. I am looking for a more secure and direct call. Here is my SP
ALTER PROCEDURE [dbo].[GetBackUpStatus]
-- Add the parameters for the stored procedure here
#linkedServerName AS VARCHAR(100),
#exemptDB as VARCHAR(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
DECLARE #Sql varchar(8000)
SET NOCOUNT ON;
set #Sql = 'select * from openquery (' + #linkedServerName + ' , ''SELECT SERVERPROPERTY(''''SERVERNAME'''') AS "SERVERNAME",
T1.NAME AS DatabaseName,
MAX(T2.backup_finish_date) AS last_db_backup_date,
CAST(COALESCE(DATEDIFF(second, MAX(T2.backup_finish_date) , CURRENT_TIMESTAMP)/ 3600.0, 9999.0) as NUMERIC(6,2)) AS [Hours Since Backup]
FROM master.sys.databases T1
LEFT OUTER JOIN msdb.dbo.backupset T2 ON T2.database_name = T1.NAME
WHERE T1.NAME NOT IN (''''tempdb'''')
GROUP BY T1.NAME
ORDER BY T1.NAME'')'
Exec (#Sql)
END
the purpose of this query is to get the server status and its database, I don't like this because of that confusing single quotes, this query will eventually grow as I develop and add function calls.
I tried this and something like this is what I wanted, since it is direct query and cleaner without those quotes. That's how I typically use linked server.
Select * from [' + #linkedservername + '].[schema].table
thanks
Solution for a large scale data model with hundreds of tables / objects.
Dynamic modification and cloning of a stored procedure for every linked-server.
It is kinda hiding any dynamic SQL under the hood.
How to
Create a stored procedure which interacts with an existing linked-server.
During a database deployment process:
Obtain the source code of the stored procedure.
Replace the name of the linked-server in the code.
If you want to create a new stored procedure (cloned), replace the name of the initial stored procedure in the code.
Create a cloned stored procedure or modify the current.
Repeat all steps for each required linked-server.
There are another variations for it.
Now, any external logic may decide which procedure to use.
You can check the existence of a linked-server or its related stored procedure.
For modifications and cloning, it is possible to use SQL Server or external tools, such as C#, CMD, etc.
For creation under SQL Server.
Before I start, I need to point out that I am a SQL noob. I can write basic statements, but anything past JOIN statements is probably fairly new to me.
That said, I have cobbled together a script that deletes records from tables. The script itself does what it needs to do; however, when I run this script, I change the "USE" line to whatever database is next, stepping through databases manually. I use a command which populates a temporary table with a list of database names as reference.
How can I run my script against each database name in the temporary table directly, preferably all from a single stored procedure?
Well, one option is that you can use a cursor to grab all the database names (exclude databases you don't want to execute on), and use dynamic sql to execute for each database. It unfortunately has to be "dynamic" since you can't just do a while loop, USE #dbname /*your magic code*/ Fetch Next from MyCursor into #dbname... It errors out when trying to do USE #dbname, So you actually do have to use exec #variable in it.
DECLARE #dbname varchar(max)
DECLARE #executeme nvarchar(max)
DECLARE DBCursor CURSOR FOR
SELECT Name
FROM sys.databases
WHERE name not in ('master', 'tempdb', 'model', 'msdb'); --add additional exclusions here
OPEN DBCursor;
FETCH NEXT FROM DBCursor INTO #dbname;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #executeme =
N'use '+#dbname+'
--YOUR SCRIPT HERE
'
EXEC sp_executesql #executeme
FETCH NEXT FROM DBCursor into #dbname;
END;
CLOSE DBCursor;
DEALLOCATE DBCursor;
GO
This is going to loop across all databases, execute your script in each, until there is none left in the list of databases. You can exclude certain databases in the select statement for the cursor (like the master, etc.) and add in any additional logic as you see fit for excluding.
Additionally, you can implement this in a stored procedure, so all you have to do is run the stored procedure, sit back and drink your favorite drink while it does the heavy lifting for you ;)
I have three websites which uses an abstract database structure with tables like: Items, Places, Categories, etc... and stored procedures like GetItemsByCategory, GetRelatedItems, etc... Actually im using exactly the same database structure for these 3 different websites.
From a code perspective im using the same code for all websites (except the HTML which is specific foreach one), and all the common code is in few projects used by all websites, so everytime that i detect a bug (which is in all websites) i just fix it on one place (the common part used by all) and automatically all websites get the fix.
Actually im using Asp.net MVC3 and Sql server.
Everytime i want to extend some funcionality, and i need a new table, stored procedure or something related with database, i have to do the modification in each database.
Do you know any approach that i could use to be able to have the same flexibility and do database modifications only one time for all websites?
Do you think I'm using a good approach or i should use something different in your opinion?
If the databases are on a single server, you could generate the script for the procedure from Management Studio, and make sure to use the option to "check for object existence" (Tools > Options > SQL Server Object Explorer > Scripting). This will yield something like this (most importantly it produces your stored procedure code as something you can execute using dynamic SQL):
USE DBName;
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF NOT EXISTS (...)
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE dbo.whatever ...
'
END
GO
Now that you have this script, you can modify it to work across multiple databases - you just need to swipe the #statement = portion and re-use it. First you need to stuff the databases where you want this to work into a #table variable (or you can put this in a permanent table, if you want). Then you can build a command to execute in each database, e.g.
DECLARE #dbs TABLE (name SYSNAME);
INSERT #dbs(name) SELECT N'db1';
INSERT #dbs(name) SELECT N'db2';
INSERT #dbs(name) SELECT N'db3';
-- now here is where we re-use the create / alter procedure command from above:
DECLARE #statement NVARCHAR(MAX) = N'CREATE PROCEDURE dbo.whatever ...
';
-- now let's build some dynamic SQL and run it!
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + '
EXEC ' + QUOTENAME(name) + '.dbo.sp_executesql N''' + #statement + ''';'
FROM #dbs;
EXEC sys.sp_executesql #sql;
Alternatively, you could create a custom version of my sp_msforeachdb or sp_ineachdb replacements:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
I used to use a tool called SQLFarms Combine for this, but the tool doesn't seem to exist anymore, or perhaps it has been swallowed up / re-branded by another company. Red Gate has since produced SQL Multi Script that has similar functionality.
If you added a column to all your tables called websiteId you could just have one database. Store the unique websiteId in each site's web.config and just pass it with each request for data. Obviously each site's data is stored with their websiteId so data can be queried per website.
It means a bit of refactoring in your db and any calls to your your db, but once done, you only have one database to maintain.
Of course this is assuming your databases are on the same server...
I am having the stored procedure. For that i need to pass the Database name as the paramters from another application or another SP. I know the approach of dynamic SQL, something like,
Create procedure mysp(#dbname varchar(20))
as
begin
declare #sql varchar(max)
set #sql='select * from '+#dbname+'.dbo.table'
end
exec mysp 'mydb'
But i dont want the SQL statements as a string. Because in my SP, i have many Sql statements are coming (Not like this only SELECT statement). so can i use,
USE DatabaseName
inside the stored procedure, so that i can use the db name in the sql statements directly without making it as string. Or any other approach is there.
My requirements, only for db name, i dont want the entire the sql statement to be dynamic...
please help me out.
Thanks in advance.
You can add the USE instruction to the dynamic query you are creating. Then you can work with that database's tables and other objects without the qualifier (within the dynamic query):
Create procedure mysp(#dbname varchar(20))
as
begin
declare #sql varchar(max)
set #sql='use '+#dbname;
set #sql=#sql + ';select ... from dbo.table1';
set #sql=#sql + ';update dbo.table2...';
set #sql=#sql + ';insert into dbo.table3...';
...
exec(#sql);
end
exec mysp 'mydb'
However, while you can do that, it's not something that you should do, unless you really have to. You are probably trying to avoid creating the same procedure in different DBs, but you may be getting you other problems with this approach, or robbing yourself of some advantages you might otherwise have without resorting to dynamic queries in SPs.
No, USE isn't allowed in stored procedures, functions and triggers.
A stored procedure is supposed to be local to the database. To access another database, there is one way (as far as I know), and it's the one you used.
I have a stored procedure that creates a database (ex: sp_createDB). In the similar fashion I have to create a 100's of databases using that one. So that I have to put it in a loop. But I don't know how to do it sqlserver. How can I do this in sqlserver.
Help me in this regard.
Thanks
You can use ...
exec sprocName
GO 100
See more here. In general, it's ...
GO [COUNT]
See, MSDN.
JP's answer is correct technically (GO [COUNT] can be used to repeat a batch of statements COUNT times), but there's a logical mistake. Your stored procedure will need different parameters everytime it executes (as it has to create unique DBs, right?), so you'll have to loop around using WHILE like this -
DECLARE #Counter INT
SET #Counter = 1
DECLARE #DBName VARCHAR(20)
WHILE (#Counter <= 100)
BEGIN
PRINT #Counter
#DBName = 'DB' + CAST(#Counter AS VARCHAR)
EXEC dbo.CreateDB #DBName
END
GO
A while loop would be fine to call it N times as others have suggested, but...
DO NOT NAME YOUR PROCEDURES SP_ ...
DO NOT NAME YOUR PROCEDURES SP_ ...
DO NOT NAME YOUR PROCEDURES SP_ ...
Within Sql Server, "sp_ ..." is reserved for system stored procedures, and could confuse people familiar with that convention! It could cause issues if Sql Server ever implements their own "sp_createDB" Procedure. Also, stored procedures will run fractionally slower if they start with a prefix of sp_ . This is because SQL Server will look for a system stored proc first. As a result, it NOT recommend to start stored procs with a prefix of sp_