Archive
Search a String in Stored Procedure, View, Function or other database object definitions
Looks like a simple topic and some might wonder why another post as this information is already available in other forums and blogs. But here I’ll go ahead with the topic and talk about an issue that not many people are aware of.
There are times when you want to search a particular string or keyword in various database objects like Stored Procedures, Views, Functions, etc. What you do in that case?
#1. Go to object explorer, open up each object and search the string by scripting it out.
– OR –
#2. Query the system catalog tables or metadata views.
Option #1 is time consuming, slow and very cumbersome.
Option #2 is quick and easy in retrieving the information.
So, we will see in what all ways we can get the required information from our database & which is the best way.
As I said at the top of this post if you search online you will see lot of solutions, but all those solution have some minor hidden issues which we will see here.
We will check below how we can search the “[HumanResources].[Employee]” table getting used in what all objects. (Here we are not checking the dependency on objects, but just checking any text which can be an object, SQL reserved keyword or user specified string to build a SQL query or a T-SQL script.)
–> 1. The first way comes in people mind is to search with INFORMATION_SCHEMA.ROUTINES View:
USE [AdventureWorks2012] GO select ROUTINE_DEFINITION, * from INFORMATION_SCHEMA.ROUTINES where ROUTINE_DEFINITION like N'%[[]HumanResources].[[]Employee]%'
6 Records returned with following Stored Procedure names: ROUTINE_NAME ufnGetContactInformation uspGetEmployeeManagers uspGetManagerEmployees uspUpdateEmployeeHireInfo uspUpdateEmployeeLogin uspUpdateEmployeePersonalInfo
–> 2. The second way is to search in sys.syscomments View:
select CASE WHEN OBJECTPROPERTY(id, 'IsProcedure') = 1 THEN 'Stored Procedure' WHEN OBJECTPROPERTY(id, 'IsView') = 1 THEN 'View' WHEN OBJECTPROPERTY(id, 'IsScalarFunction') = 1 THEN 'Scalar Function' WHEN OBJECTPROPERTY(id, 'IsTableFunction') = 1 THEN 'Table Valued Function' WHEN OBJECTPROPERTY(id, 'IsInlineFunction') = 1 THEN 'Inline Function' WHEN OBJECTPROPERTY(id, 'IsTrigger') = 1 THEN 'Trigger' END AS ObjType, * from sys.syscomments where text like N'%[[]HumanResources].[[]Employee]%'
This query returns 12 reocrds, with 1 Table Valued Function, 5 SPs, 1 Trigger & 5 Views.
–> 3. The third way is to search in sys.sql_modules View:
select CASE WHEN OBJECTPROPERTY(object_id, 'IsProcedure') = 1 THEN 'Stored Procedure' WHEN OBJECTPROPERTY(object_id, 'IsView') = 1 THEN 'View' WHEN OBJECTPROPERTY(object_id, 'IsScalarFunction') = 1 THEN 'Scalar Function' WHEN OBJECTPROPERTY(object_id, 'IsTableFunction') = 1 THEN 'Table Valued Function' WHEN OBJECTPROPERTY(object_id, 'IsInlineFunction') = 1 THEN 'Inline Function' WHEN OBJECTPROPERTY(object_id, 'IsTrigger') = 1 THEN 'Trigger' END AS ObjType, * from sys.sql_modules where definition like N'%[[]HumanResources].[[]Employee]%'
Same as above, returns 12 reocrds.
No issue till now, great!!!
Now, let’s check this scenario: I want to get all Database Objects containing the “[BikeFrame]” column name.
–> 1. When I choose to search with INFORMATION_SCHEMA.ROUTINES view it does not give any result:
select ROUTINE_DEFINITION, * from INFORMATION_SCHEMA.ROUTINES where ROUTINE_DEFINITION like N'%[[]BikeFrame]%'
Because this View stores only first 4000 characters in ROUTINE_DEFINITION column of NVARCHAR datatype. So the query gets truncated before the column name occurs.
–> 2. When I go with sys.syscomments view it also gives me nothing:
select * from sys.syscomments where text like N'%[[]BikeFrame]%'
Here this View stores the query-text in “text” column of NVARCHAR type with size of 4000 characters. The query also gets truncated here but it stores the query in chunks thus the rest part goes to the next rows.
So it should return the row with matching chunk from the matching row, but it does not, why? Because in this typical case the column name is getting splitted in different rows. Check by running this query:
select id, colid, ctext, text from sys.syscomments where id = 2059154381
Output:- id colid ctext text 2059154381 1 0x0D000A00430052004... CREATE VIEW [Production].[vProductModelCatalog... 2059154381 2 0x65004600720061006... eFrame] ,[CatalogDescription].value(N'declare nam...
Here, you can see the second half text “eFrame]” of the column name is placed at beginning of the 2nd row.
And if you expand the “text” column you will see the first half text “[Bik” of the column name at the end of 1st row.
–> 3. When I go with sys.sql_modules view it gives me expected result:
select * from sys.sql_modules where definition like N'%[[]BikeFrame]%'
Because this View stores the query text in “definition” column of NVARHCAR(MAX) type with length of 2GB.
So, its better to use sys.sql_modules view to do all metadata based searches instead of INFORMATION_SCHEMA.ROUTINES & sys.syscomments views.
“Identity Gap” Issue with the new SEQUENCE feature in SQL Server 2012 … and its workaround
In my previous post I discussed about an issue with IDENTITY property. Here today while working on a similar new feature “SEQUENCE”, I found a similar kind of behavior with it.
Here also when you restart SQL Server or restart the service the last sequence number jumps to a higher random number.
Here is a simple code to reproduce this issue:
-- CREATE a simple Sequence CREATE SEQUENCE CountBy1 START WITH 1 INCREMENT BY 1 MINVALUE 0 NO MAXVALUE ; GO -- CREATE a test table: CREATE TABLE TEST_SEQ (ID INT, NAME VARCHAR(200)); -- INSERT some records: INSERT INTO TEST_SEQ SELECT NEXT VALUE FOR CountBy1, 'Manoj Pandey' INSERT INTO TEST_SEQ SELECT NEXT VALUE FOR CountBy1, 'Gaurav Pandey' GO -- Check the INSERTed records before server restart: SELECT * FROM TEST_SEQ GO -- RESTART SQL Server & INSERT a new record: INSERT INTO TEST_SEQ SELECT NEXT VALUE FOR CountBy1, 'Garvit Pandey' GO -- Check the INSERTed records after server restart: SELECT * FROM TEST_SEQ GO --// Final cleanup DROP TABLE TEST_SEQ DROP SEQUENCE CountBy1
Finally I got the following output:

As you can see by running the above test before I restarted SQL Server the SEQUENCE value assigned to the last record was 2, but when I restarted the new SEQUENCE value generated is 51.
Reason: Actually while creating SEQUENCE object SQL Server engine caches the new SEQUENCE values to Increase performance. By default the cache size is 50, so it caches values upto 50 values, and when SQL Server restarts it starts after 50, that’s a bug.
Workaround: To avoid this situation you can put an “NO CACHE” option while declaring the SEQUENCE object, like:
-- CREATE a simple Sequence CREATE SEQUENCE CountBy1 START WITH 1 INCREMENT BY 1 MINVALUE 0 NO MAXVALUE NO CACHE ; -- here GO
This will not cache the future values and you wont get this issue of jumping values and gaps.
To know more about SEQUENCES check my previous blog post, [link].
Jump/Gap Issue with IDENTITY property in SQL Server 2012 … and its workaround (not a bug)
Sometime back there was discussion going on in an SQL Server forum regarding issues with IDENTITY property in the new SQL Server 2012. The issue was that, when restarting SQL Server (or service) the IDENTITY value in a particular table having IDENTITY column jumps to a higher random number and starts from there.
I tried to reproduce this issue, the SQL Server version I’m using is as follows:
Microsoft SQL Server 2012 RC0 - 11.0.1750.32 (X64) Nov 4 2011 17:54:22 Copyright (c) Microsoft Corporation Business Intelligence Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1)
I reproduced the issue with following code:
-- CREATE a test table:
CREATE TABLE TEST_IDENTITY (
ID INT IDENTITY(1,1),
NAME VARCHAR(200)
);
-- INSERT some records:
INSERT INTO TEST_IDENTITY (NAME)
SELECT 'Manoj Pandey'
INSERT INTO TEST_IDENTITY (NAME)
SELECT 'Gaurav Pandey'
GO
-- Check recently inserted records:
SELECT * FROM TEST_IDENTITY -- 2 records, with ID value 1, 2.
-- Check the current IDENTITY value:
DBCC CHECKIDENT ('TEST_IDENTITY')
--Checking identity information: current identity value '2', current column value '2'.
--DBCC execution completed. If DBCC printed error messages, contact your system administrator.
GO
-- RESTART SQL Server and check the current IDENTITY value:
DBCC CHECKIDENT ('TEST_IDENTITY')
--Checking identity information: current identity value '11', current column value '2'.
--DBCC execution completed. If DBCC printed error messages, contact your system administrator.
-- INSERT a new record:
INSERT INTO TEST_IDENTITY (NAME)
SELECT 'Garvit Pandey'
GO
-- Check recently inserted records:
SELECT * FROM TEST_IDENTITY -- 3 records, with ID value 1, 2, 12.
GO
--// Final cleanup
DROP TABLE TEST_IDENTITY
Finally I got the following output:

As you can see by running the above test before I restarted SQL Server the IDENTITY value was 2, but when I restarted the IDENTITY value changed to 11.
Thus the new records was inserted with value = 12.
The above bug/issue has been logged in the Microsoft Connect site, [here].
Workaround: Right now there is no fix for this, so you need to check all tables in you database every time your SQL Server restarts and reseed the correct IDENTITY value. Check my blog post to reseed the IDENTITY value.
Reorganize Index vs Rebuild Index in SQL Server
Well-designed indexes on tables/views improves the performance of queries run against a database by reducing disk I/O operations and consume fewer system resources. Indexes can be helpful for a variety of queries that contain SELECT, UPDATE, DELETE, or MERGE statements.
SQL Server Database Engine automatically maintains indexes whenever INSERT, UPDATE, or DELETE operations are made to the underlying data. Over time these modifications can cause the information in the index to become scattered/fragmented in the database. Fragmentation exists when indexes have pages in which the logical ordering, based on the key value, does not match the physical ordering inside the data file. Heavily fragmented indexes can degrade query performance and cause your application to respond slowly.
SQL Server has provided ways to reduce/remedy fragmentation by Reorganizing or Rebuilding an Index.
1. Reorganize Index: uses minimal system resources. It defragments the leaf level of clustered and nonclustered indexes on tables and views by physically reordering the leaf-level pages to match the logical, left to right, order of the leaf nodes. Reorganizing also compacts the index pages. Compaction is based on the existing fill factor value.
2. Rebuild Index: drops and re-creates the index. This removes fragmentation, reclaims disk space by compacting the pages based on the specified or existing fill factor setting, and reorders the index rows in contiguous pages. When ALL is specified, all indexes on the table are dropped and rebuilt in a single transaction.
So, which approach to go with, Reorganize or Rebuild?
First of all we’ll check the fragmentation status of a particular table or an index, by using sys.dm_db_index_physical_stats function.
Here we will check the status of all indexes in [HumanResources].[Employee] table of [AdventureWorks2012] database.
select
x.name as Index_Name,
s.database_id, s.object_id, s.index_id, s.index_type_desc, -- General info columns
s.avg_fragmentation_in_percent, s.fragment_count, s.avg_fragment_size_in_pages -- stats we need
from sys.indexes x
cross apply sys.dm_db_index_physical_stats (
DB_ID(N'AdventureWorks2012'), -- database_id
x.object_id, -- object_id
x.index_id, -- index_id
NULL, -- partition_number
NULL) as s -- mode
where s.object_id = object_id('HumanResources.Employee')
Output:

We will use the following criteria setup by Microsoft to detirmine the best method to correct the fragmentation:
avg_fragmentation_in_percent value | Corrective statement > 5% and <= 30% ALTER INDEX REORGANIZE > 30% ALTER INDEX REBUILD WITH (ONLINE = ON)*
The above results shows that the Indexes [AK_Employee_LoginID] and [AK_Employee_NationalIDNumber] requires Rebuild and rest of them are good.
–> TO REBUILD:
--// To Rebuild [AK_Employee_LoginID] Index, run the following query: USE AdventureWorks2012; GO ALTER INDEX AK_Employee_LoginID ON HumanResources.Employee REBUILD; GO --// To Rebuild All indexes, use following query: USE AdventureWorks2012; GO ALTER INDEX ALL ON HumanResources.Employee REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = ON, STATISTICS_NORECOMPUTE = ON); GO
–> TO REORGANIZE:
--// To Reorganize [AK_Employee_NationalIDNumber] Index, run the following query: USE AdventureWorks2012; GO ALTER INDEX AK_Employee_NationalIDNumber ON HumanResources.Employee REORGANIZE; GO --// To Reorganize All indexes on [HumanResources].[Employee], use following query: USE AdventureWorks2012; GO ALTER INDEX ALL ON HumanResources.Employee REORGANIZE; GO
So, check the fragmentation status by using the DM function sys.dm_db_index_physical_stats and then decide to do either REORGANIZE or a REBUILD on an Index.
Difference between Decimal and Numeric datatypes
Decimal vs Numeric: None… both are same!!!
This is a misconception among many developers that Decimal and Numeric are different data types. And lot of people do not agree to understand that they are same unless I redirect them to this MSDN BOL [link].
Few days back while designing a data model for one our support enhancement project this question was raised and following was the conversation:
Person A: Ok, this column is defined as Numeric, can’t we make it a Decimal?
Me: What difference will it make?
Person A: Decimal is more precise than Numeric (according to him).
Me: No, both are same.
Person B, C and some others: There is slight difference, Decimal holds more accurate precision values than Numeric.
To my surprise none of them were aware that both are same.
So, when I redirected them to MSDN decimal and numeric (Transact-SQL) and show them the line which says: “numeric is functionally equivalent to decimal.” they agreed.
So, not to confuse between Decimal & Numeric, they both are synonyms to each other.




