Anybody who’s ever had to work with a SQL Server object without being told which database it lives in, or worked in a huge schema - like Great Plains or SAP - should find this post interesting. There are several ways to search for a database object by its name or type; we’ll examine a few options, look into the issues involved, and present code to accomplish our search.
The Code
Set NoCount On
Declare @dbName varChar(200), @ins varChar(1000)–Internal use only
Declare
@searchName varChar(200),
@objType varChar(2),
@objTypeList varChar(300),
@owner varChar(200),
@excludeName varChar(100),
@ignoreDatabases varChar(1000),
@sort varChar(1000)
Set @searchName = ‘Log’
/*Set @objTypeList = ‘U, P, V, FN, TR’
Set @objType = ‘P’
Set @owner = ‘dbo’
Set @excludeName = ‘Backup-Log’*/
Set @searchName = IsNull(@searchName, ”)
Set @sort = IsNull(@sort, ‘DatabaseName, SchemaOwner, SqlObjectType’)
Set @ignoreDatabases = IsNull(@ignoreDatabases, ‘model, msdb, tempDB, master, ReportServerTempDB, ReportServer’)
Create Table #sqlSearchResults ( DatabaseName varChar(200), SchemaOwner varChar(100), ObjectName varChar(200), SqlObjectType varChar(2) )
Declare c Cursor Forward_Only For
Select name From master.dbo.sysDatabases
For Read Only
Open c
Fetch Next From c Into @dbName
While @@Fetch_Status = 0 Begin
If PatIndex(’%’ + @dbName + ‘%’, @ignoreDatabases) <= 0 Begin
Set @ins = ‘Insert Into #sqlSearchResults Select ”’ + @dbName + ”’ As DatabaseName, IsNull(U.Name, ”???”), O.Name, xType From ‘ + @dbName + ‘.dbo.sysObjects O Left Join ‘ + @dbName + ‘.dbo.sysUsers U On O.uid = U.uid Where (O.Name Like ”’ + ‘%’ + @searchName + ‘%”)’
If @objType Is Not Null Set @ins = @ins + ‘ And (xType = ‘ + ”” + @objType + ”’)’
If @owner Is Not Null Set @ins = @ins + ‘ And (U.Name = ‘ + ”” + @owner + ”’)’
If @objTypeList Is Not Null Set @ins = @ins + ‘ And (”’ + @objTypeList + ”’ Like ”%” + rTrim(xType) + ”%”)’
If @excludeName Is Not Null Set @ins = @ins + ‘ And (O.Name Not Like ‘ + ”’%’ + @excludeName + ‘%”)’
Exec(@ins)
End
–Print @ins
Fetch Next From c Into @dbName
End
Close c
DeAllocate c
Exec(‘Select * From #sqlSearchResults Order By ‘ + @sort)
Drop Table #sqlSearchResults
Arguments
There are seven variables you can set to control the search. All are optional; passing nothing in will return a list of all objects in all non-system databases.
- @searchName - Find an object with this string in its name.
- @objType - Only look for objects of specific types. This can be ignored in favor of parameter #3, although it’s provided because, in very large databases, a match using the equality ( = ) operator instead of the LIKE operator will give somewhat better performance.
- @objTypeList - Allows you to search for tables and views, but nothing else … or triggers, procedures, and constraints. Simply build a list of xType values - instead of only one using @objType - that you want to see results for.
- @owner - The object’s owner or schema. If you only want to see results belonging to dbo, set @owner to dbo.
- @excludeName - Provided to avoid useless matches. If you’re looking for any object with the word user, but don’t want to see the sysUsers table from each database you search, set @excludeName to sysUsers.
- @ignoreDatabases - Similar to #5, you can avoid false positives and improve performance by restricting your search to databases of interest. Unlike #5, this can be a list; SQL’s Pat[tern]Index function is used to determine a match, so you can use any delimiter you like.
- @sort - How you’d like the search results sorted.
How it Works
A temp table - #sqlSearchResults - is used to hold matches. Temporary tables are specific to the connection using them, so two people can run this code at the same time, and each have their own individual ( session based ) results table. A cursor is used to construct an insert statement logging matches from each database. Inside the cursor loop, the Exec command is used to issue a dynamic insert command populating our temp table. Finally, another Exec command selects the data back out of our search results table, ordered by whatever column or expression list the user wants.
This was designed to be a stored procedure, so, for extra credit, we’ll look into how to promote it from a humble query window script to a global proc. Stored procedures in the master database are available from any database on the server, so this is a straightforward change. Of the two declare statements, one is marked for internal use only, while the second is indented like parameters. Simply promote these when you wrap the code with a Create Procedure command.
Multiple/All Databases
SQL Server allows any number of databases to be hosted on one server: we can easily search one of them, but the question becomes how to search them all. To be useful, we only want one result set, rather than one per database.
sp_msforeachdb
An undocumented system stored procedure, sp_msForEachDB runs a dynamic query in your databases. Theoretically this happens in each db on a server, although there seem to be unknown limitations. More importantly, undocumented features in software development tools are generally meant for internal use only, not supported to the public, and may change or even vanish in any future version.
A Cursor
Cursors let you iterate through a result set, row-by-row. Each record can be evaluated, even changed, in a procedural manner. Instead of applying clever formulas in a select list - the set-based approach - a cursor lets you use looping, branch control, other queries or stored procedures, and other concepts for every row in a query.
This is a familiar concept to anyone who’s developed software using virtually any procedural language ( C and its variants, VB, PHP, etc ) but somewhat alien to relational theory.
System Tables
Even in humble Access, Microsoft has shown they like storing metadata in relational format. SQL Server provides a number of information schema views that can simplify the type of work we’re doing. If the code above didn’t allow users to search for any type of SQL object, the system views might be a better choice.
Master..SysDatabases
The sysDatabases table in the master database has a row for every database the server maintains. We only use the name column in the script above, although crdate and cmptlevel can also be interesting. In the search code, the magic is Declare c Cursor For Select Name From sysDatabases. Interestingly, the three tables being used in this query are available in both the dbo and sys schema.
SysObjects
This exists in every database. Every object in the db has a row in this table, giving its: name, type, parent ( triggers and constraints are ‘children’ of the tables they apply to ) and the owner or schema. The xType column is a char(2) value - this is important because single-character values are padded with a space - whose most important values are
- U
- User table
- V
- View
- P
- Stored Procedure
- TR
- Trigger
- FN, IF, TF
- User Defined Functions ( UDFs ) broken out as scalar, inline table, and table valued
- PK, FK
- Primary and Foreign Keys
SysUsers
Again, this table exists in every database, and is joined to sysObjects to find the schema an object ‘lives’ in. Of interest here are uid and name.
John on 01 Feb 2008 at 5:01 pm #
How incredibly cool! I put in this line of code right before you drop the table
select * from #sqlSearchResults as Match for xml auto, elements, root(’SearchResults’)
Then I can take that and XSLT it into a web page.
Even ASP Ancient Can Do XML With Access « Alexander The Great on 03 Feb 2008 at 5:42 am #
[…] is a legacy tip - SQL Server has better support for XML by far, though, without even needing an external language like ASP. And SQL Server is free - Access […]
Petteri on 06 Feb 2008 at 12:38 am #
why not just run this query, copy the results and paste them into a new window, then run that as a query?
Select ‘Select Name, ”’ + Name + ”’ As FoundIn From ‘ + Name + ‘..sysObjects Where name = ‘’search_text”’ From sysDatabases
Forrest on 10 Feb 2008 at 6:56 am #
Well, that works, too, but the point is being able to do everything in one request … automation.
Jean Thierry on 21 Feb 2008 at 6:24 pm #
Nice work! Fantastic query. Search is one of the most frequently used options when it comes to dig deep into the database structure. Of course, it’s not that type of tasks that you perform on a daily basis but nevertheless, it happens that you have to search though the schema to get results really fast. And this is one of situations when you need to do it right now and you have to act immediately. Unfortunately here is where basic management facilities can’t handle the tasks you want them to help you with. It’s a paradox but while the fact is everything pretending to be a serious solution relies on SQL we still think that we don’t have a way to effectively solve our SQL-tied tasks. As an example, I thought it’s impossible to find every object in SQL server having a specific ACL or a specific security assigned to it. I frequently need to find obsolete security settings that were specified for stored procedure or specific object for the user that has been resigned with our company. Sometimes it’s also needed to clean out security to restrict access to SQL item for the user that moved from one product to another. But I couldn’t find any solution or advice other than just recommendations to script it myself. But as a developer you know what it takes to create any program aimed for security management. It’s a great deal of work even for a professional developer. Imagine what would it take from me if I had to develop such tool. Others that understood the complexity of the problem were telling me that I have to forget about this as security management on SQL is a very complicated task and there’s no single management tool on Earth able to do what I want. And you know what, we all were completely wrong. Quite recently I came across a wonderful security management tool from Scriptlogic called Security Explorer http://www.scriptlogic.com/products/security-explorer/ that does all that I wanted and even more. That was an eyeopener! With such a small tool as Security Explorer is I could make a search throughout any of the SQL servers I ran and get a result with the permissions that I have defined for some objects within a few seconds. Sure the time it takes to find the permissions depends on the size of SQL databases and probably also on how distributed is your SQL farm. But with such databases as mine it works with lightning speed. I guess we may thank .NET request efficiency shown there. As far as I understand the tool works through it as I see calls in SQL profiler. Today when I also found your brilliant SQL script I can tell for sure there’s nothing impossible in SQL world!