SQL Server : find all stored procedures containing Left(column,5) - sql-server

I'm using SQL Server 2014 and I need to find all stored procedures that use a Left(Column, 5) function
For example, it should find
left(jobno, 5)
but not
left(jobno, 50)
it should find
left(ZIP, 5)
I initially thought I could use
Like '%Left(%,5)%'
but it returns no records
Thanks
mark

This is a bit costly approach, but is serves the purpose. It will work when Length to LEFT function is specified as Number, but fails when Length is specified using variable or some other expressions. Hope this helps.
DECLARE #Search VARCHAR(10) = '5'
;WITH CTE AS
(
SELECT 1 AS Lvl, o.name, o.type, o.Object_ID
,NULLIF(CHARINDEX('left(',definition),0) AS LeftPos
,CHARINDEX(')', definition, (NULLIF(CHARINDEX('left(',definition),0))) AS LastIndex
,LTRIM(RTRIM(SUBSTRING(definition, CHARINDEX(',', definition, (NULLIF(CHARINDEX('left(',definition),0))) + 1, CHARINDEX(')', definition, (NULLIF(CHARINDEX('left(',definition),0))) - CHARINDEX(',', definition, (NULLIF(CHARINDEX('left(',definition),0))) -1 ))) AS LeftLen
FROM sys.all_sql_modules m
INNER JOIN sys.objects o on o.Object_ID = m.Object_ID
WHERE CHARINDEX(')', definition, (NULLIF(CHARINDEX('left(',definition),0))) - CHARINDEX(',', definition, (NULLIF(CHARINDEX('left(',definition),0))) -1 > 0
UNION ALL -- get all occurunces or Left
SELECT 2 AS Lvl, o.name, o.type, o.Object_ID
,NULLIF(CHARINDEX('left(',definition,LastIndex),0) AS LeftPos
,CHARINDEX(')', definition, (NULLIF(CHARINDEX('left(',definition,LastIndex),0))) AS LastIndex
,LTRIM(RTRIM(SUBSTRING(definition, CHARINDEX(',', definition, (NULLIF(CHARINDEX('left(',definition,LastIndex),0))) + 1, CHARINDEX(')', definition, (NULLIF(CHARINDEX('left(',definition,LastIndex),0))) - CHARINDEX(',', definition, (NULLIF(CHARINDEX('left(',definition,LastIndex),0))) -1 ))) AS LeftLen
FROM sys.all_sql_modules m
INNER JOIN sys.objects o ON o.Object_ID = m.Object_ID
INNER JOIN CTE c ON c.Object_ID = m.Object_ID
AND c.LastIndex IS NOT NULL AND ISNULL(c.LeftLen,#Search) <> #Search
WHERE CHARINDEX(')', definition, (NULLIF(CHARINDEX('left(',definition,LastIndex),0))) - CHARINDEX(',', definition, (NULLIF(CHARINDEX('left(',definition,LastIndex),0))) -1 > 0
)
SELECT * FROM CTE
WHERE LeftLen = #Search
OPTION (MAXRECURSION 10000);

SQL unfortunately cannot have wild cards in the middle of a string. Try this
(column name) like '%left(%' and (column name) like '%,5)%'
That should give you the desired result

This is one of the reasons why we've implemented regex using the SQL CLR. However, you can do a couple other things to solve your issue.
You could do something like this ...
select *
from sys.objects
where object_definition(object_id) like '%left(%,5)%';
Or something like this, which seems a bit cleaner, however, sometimes I use the above method, because I need some other stuff that spans from sys.objects ...
select *
from sys.sql_modules
where definition like '%left(%,5)%';
Another alternative is to come up with a list of possible column names. If you don't have any cross database worries, then you may want to use sys.columns, sys.all_columns or even sys.parameters as your pick list. To do this, you'd do something like this ...
select *
from sys.sql_modules m
cross join (
select distinct name
from sys.all_columns) c
where definition like '%left(' + c.name + ',5)%';
One other thing to consider is whitespace, which, again, is another reason to implement regex using SQL CLR. For instance, in the above case, somebody may put a space between the "," after the column name and the number "5". But, with that being said, the above queries should get you really close.
Hope this helps.

You could use something like
LIKE '%LEFT([a-z],5)%'

Related

Identify how many times a string exists on a stored procedure in SQL Server

I have to search the occurrence of a particular string inside all available stored procedures in SQL Server. I know that we can get this by using the below query.
SELECT OBJECT_NAME(OBJECT_ID) PrcName
FROM sys.sql_modules
WHERE DEFINITION LIKE '%SearchStr%'
But is there a way we can find out how many times the particular string is available in each stored procedure? This is for estimating the effort modifying the stored procedures.
Any help will be much appreciated.
This will work as tested:
;WITH cte as
(
SELECT OBJECT_NAME(OBJECT_ID) PrcName, OBJECT_ID
FROM sys.sql_modules
WHERE DEFINITION LIKE '%tblNDT%')
select t1.PrcName, (LEN(Definition) - LEN(replace(Definition,'tblNDT',''))) / LEN('tblNDT') Cnt
from cte t1
INNER JOIN sys.sql_modules t2 on t1.object_id = t2.object_id
An easy way of checking how many times something occurs is to take the initial length, replace your string with blanks, recheck the length, and divide by the length of your string:
DECLARE #sentence VARCHAR(100)
DECLARE #word VARCHAR(100)
SET #word = 'Cool'
SET #sentence = 'This cool sentence is really cool. Cool!'
DECLARE #wordlen INT = (SELECT LEN(#word))
--Original sentence and length
SELECT #sentence AS setencelen
SELECT LEN(#sentence) AS origsentence
--With word removed
SELECT REPLACE(#sentence, 'cool', '') AS shortenedsentence
SELECT LEN(REPLACE(#sentence, 'cool', '')) AS shortenedlen
SELECT LEN(#sentence) - LEN(REPLACE(#sentence, 'cool', '')) AS diffinlength
SELECT (LEN(#sentence) - LEN(REPLACE(#sentence, 'cool', ''))) / #wordlen AS occurrences
I have seen this work in some cases and not in others. If you have a bunch of comments that contain the same string, it will count incorrectly.
I have found a solution for this.
DECLARE #cnt AS INT= 1
DECLARE #SearchStr VARCHAR(MAX) = 'SearchText'
;WITH CTE_SearchStr1
AS
(
SELECT #cnt Cnt, #SearchStr SearchStr UNION ALL
SELECT Cnt + 1, #SearchStr+'%'+SearchStr FROM CTE_SearchStr1
),
CTE_SearchStr2
AS
(
SELECT TOP 100 * FROM CTE_SearchStr1
)
SELECT OBJECT_NAME(OBJECT_ID) ObjectName, MAX(cnt) cnt FROM sys.sql_modules a INNER JOIN CTE_SearchStr2 b ON
a.definition LIKE '%'+b.SearchStr+'%'
GROUP BY OBJECT_NAME(OBJECT_ID) ORDER BY 2 DESC
Only problem with the above query is that I can not search for more that 100 times. It will throw the below exception
Msg 530, Level 16, State 1, Line 3 The statement terminated. The
maximum recursion 100 has been exhausted before statement completion.
In my scenario, the number of occurrences are less than 100, but is there a way to overcome this error?

Surrounding query with parenthesis cause error For XML

When trying to wrap query for further processing I get an error by just putting parenthesis around the query. See query below, if I simply remove the wrapping parenthesis, the query works. But when I use the parenthesis I get this error:
Incorrect syntax near the keyword 'for'
(
select '; ' + [NAME]
from (select a.[NAME] as 'NAME'
from [MS].[dbo].[A_POSITIONS] a
where a.ID = 208418
except
select top 1 b.[NAME]
from [MS].[dbo].[A_POSITIONS] b
where b.ID = 208418) as c
for XML PATH(''),TYPE
)
Anyone have an idea on why this is happening?
Thanks!
Adding the parenthesis alone around your query leads to an error, but if you type a SELECT before the opening parenthesis the code will run, meaning that you can use it for further processing:
if OBJECT_ID('A_POSITIONS') is not null
drop table [dbo].[A_POSITIONS]
create table [dbo].[A_POSITIONS]([ID] int,[Name] varchar(100))
insert into [dbo].[A_POSITIONS] values
(208418, 'one'),(208418, 'two'),(208418, 'three'),(208418, 'four'),(208418, 'five')
select
(
select '; ' + [NAME]
from (select a.[NAME] as 'NAME'
from [MS].[dbo].[A_POSITIONS] a
where a.ID = 208418
except
select top 1 b.[NAME]
from [MS].[dbo].[A_POSITIONS] b
where b.ID = 208418) as c
for XML PATH(''),TYPE
)
One last consideration: TOP keyword without an ORDER BY clause is non-deterministic.
From Microsoft Docs (more info here):
When TOP is used in conjunction with the ORDER BY clause, the result set is limited to the first N number of ordered rows; otherwise, it returns the first N number of rows in an undefined order
So you should really consider adding an ORDER BY to the second part of your query:
select '; ' + [NAME]
from (select a.[NAME] as 'NAME'
from [MS].[dbo].[A_POSITIONS] a
where a.ID = 208418
except
select top 1 b.[NAME]
from [MS].[dbo].[A_POSITIONS] b
where b.ID = 208418
--Add an ORDER BY clause here
) as c
for XML PATH(''),TYPE

Searching stored procedure and views but ignoring comments

I have a query as
select
definition
from
sys.objects so
join
sys.sql_modules ssmsp on so.[object_id] = ssmsp.[object_id]
where
so.type in ('v', 'p')
where
definition like '%exec%'
While populating records, gets populated from comments also. How can I avoid getting filtered from comments?
Is there any solution?
Thanks
I think this is going to be nearly impossible to achieve in a single query.
Bear in mind that [definition] has no formatting, no line breaks, etc. the code is a single line (copy one and paste it into the editor).
If a comment starts with -- then where does it end? you have no way of knowing.
It is a little easier with /* because you can find the corresponding */ but there is still the added complication of multiple occurrences of the search string.
You might have a little more luck using PATINDEX and specifying a case-sensitive version of your collation (if you have a case insensitive database) and for example you know you only want occurrences of EXEC and not "execute" e.g. WHERE patindex('%EXEC%',defintion COLLATE SQL_Latin1_General_CP1_CS_AS) > 0
First for a fast varchar(max) string "splitter". Below is a hacked version of Jeff Moden's delimitedSplit8K.
IF OBJECT_ID('dbo.DelimitedSplit2B','IF') IS NOT NULL DROP FUNCTION dbo.DelimitedSplit2B;
GO
CREATE FUNCTION dbo.DelimitedSplit2B
(
#pString varchar(max),
#pDelimiter char(1)
)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
WITH L1(N) AS
(
SELECT N
FROM (VALUES
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),
(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(N)
), --216 values
cteTally(N) AS
(
SELECT 0 UNION ALL
SELECT TOP (DATALENGTH(ISNULL(#pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM L1 a CROSS JOIN L1 b CROSS JOIN L1 c
--2,176,782,336 rows: enough to handle 2,147,483,647 characters (the varchar(max) limit)
),
cteStart(N1) AS
(
SELECT t.N+1
FROM cteTally t
WHERE (SUBSTRING(#pString,t.N,1) = #pDelimiter OR t.N = 0)
)
SELECT
ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
Item = SUBSTRING(#pString,s.N1,ISNULL(NULLIF((LEAD(s.N1,1,1)
OVER (ORDER BY s.N1) - 1),0)-s.N1,DATALENGTH(ISNULL(#pString,1))))
FROM cteStart s;
Next for the function to search your DDL
create function dbo.SearchObjectDDLFor (#searchstring varchar(100), #maxLen int)
returns table as return
select objectName, lineNumber, lineText
from
(
select
objectName = ss.[name]+'.'+so.[name],
lineNumber = itemnumber,
lineText = substring(t.item, 1, isnull(nullif(charindex('--', t.item),0)-1, 8000)),
isLongComment =
sum
( -- this will assign a 1 for everything
case when t.item like '/*%' then 1
when t.item like '*/%'then -1
else 0 end
) over (partition by so.[name] order by itemnumber)
from sys.objects so
join sys.sql_modules ssmsp on so.[object_id] = ssmsp.[object_id]
join sys.schemas ss on so.schema_id = ss.schema_id
cross apply dbo.delimitedSplit2B(definition, char(10))
cross apply (values (rtrim(ltrim(replace(item,char(13),''))))) t(item)
where so.type in ('v', 'p')
and len(definition) < isnull(#maxLen,100000) -- character limit is #maxLen (100K default)
) splitLines
where isLongComment = 0 and lineText not like '--%' and lineText <> '*/'
and lineText like '%'+#searchstring+'%';
This function:
Accepts an input string to search for (#searchstring)
Splits your objects into lines
Returns only the portions of the line not part of a comment
filters the lines created in step3 for ones that contain #searchstring and returns the ObjectName (.), Line number and Text.
Caveats:
I just threw this together quick so forgive any errors
A t-sql splitter that accepts [n]varchar(max) will be slow. A CLR splitter would likely be faster but we're not talking about millions of rows. That said, you can speed it up by filtering the number of lines with #maxLen. #maxlen says "ignore and objects with more that #maxLen number of lines." When null it will search objects up to 100K lines long (but this can be adjusted).
This function address comments scenarios where comments look have "--" any where in the string: and scenarios where the comment is nested between "/" and "\ on separate lines.
a few scenarios which require more coding to suppress the comments include:
.
select col1, /* skipping col2 for now */ col3, col4
and
/*********
comments here
*********/
Examples:
select * from dbo.SearchObjectDDLFor('nocount', NULL);
select * from dbo.SearchObjectDDLFor('nocount', 2000);
Results will look something like :

Dynamically generate Sql for variable definition and assignment for a row in a table?

For example, I have a table
create table T (
A int,
B numeric(10,3),
C nvarchar(10),
D datetime,
E varbinary(8)
)
Update: This is just one of the example table. Any table can be used as input for generating the SQL string.
Is there an easy way to dynamically generate the following Sql for a row? (Any built-in function to make the Quotes, prefix easier?)
'declare
#A int = 1,
#B numeric(10,3) = 0.01,
#C nvarchar(10) = N''abcd'',
#D = ''10/1/2013'',
#E = 0x9123'
No, there isn't. The closest you might get, but which still will require manual changes, is by using SQL Server Management Studio. Expand the database and the table.
Right-click the table, then select Script table As, Insert To, and then selecting a new query window. This will generate output that will give you a starting point, but it's not generating variables. You'd have to either script that yourself, or edit the generated INSERT statement.
Example code:
INSERT INTO MyDB].[dbo].[Table1]
([A]
,[B]
,[C]
VALUES
(<A, int,>
,<B, float,>
,<C, nvarchar(10),>
)
GO
Not sure what specifically you are trying to achieve… There is no built in function for something like this but you can try to create one easily using query similar to this one…
select 'DECLARE #A int = ' + TableA.A +
', #B numeric(10,3) = ' + TableA.B +
', #C nvarchar(10) = N''' + TableA.C +
''', #D = ''' + TableA.D + ''''
from TableA
WHERE PrimaryKeyColumn = some_value
Just cleanup the query above and convert it into a function that returns nvarchar
If you want to dynamically generate table definitions too that’s possible too but you’ll have to use system views to create this for any given table.
Try something like this and work your way from here
select T.name, C.name, TY.name, C.column_id
from sys.tables T
inner join sys.columns C on T.object_id = C.object_id
inner join sys.types TY on TY.system_type_id = C.system_type_id
where T.name = 'TableName'
order by C.column_id asc

SQL Server SP, Function, View source line counter

Is there any utility availble to count the total lines of user created Stored Procedure, Function, Views in a Database?
For SQL Server 2005 and 2008.
This includes all code including blank lines and trailing blank lines, but not the last line (no CRLF). So it's averages out... but it would always be an approximation anyway.
WITH CRLF AS
(
SELECT
CHARINDEX('
', definition) AS CRLF,
SM.[object_ID]
FROM
sys.sql_modules SM
WHERE
OBJECT_NAME([object_ID]) not in ('fn_diagramobjects', 'sp_alterdiagram', 'sp_creatediagram', 'sp_dropdiagram', 'sp_helpdiagramdefinition', 'sp_helpdiagrams', 'sp_renamediagram', 'sp_upgraddiagrams', 'sysdiagrams')
UNION ALL
SELECT
CHARINDEX('
', definition, C.CRLF + 2),
SM.[object_ID]
FROM
sys.sql_modules SM
JOIN
CRLF C ON SM.[object_ID] = C.[object_ID]
WHERE
CHARINDEX('
', definition, C.CRLF + 2) > C.CRLF
)
SELECT
COUNT(*)
FROM
CRLF
OPTION
(MAXRECURSION 0)
Edit:
You may need OBJECTPROPERTY(SM.[object_ID], 'IsMSShipped') = 0 or explicitly exclusions for diagram code etc
Edit 2:
From other solution in otehr answer, corrected to not give "-1" for check constraints and apply same filters/types
select t.sp_name, sum(t.lines_of_code) as lines_ofcode, t.type_desc
from
(
select o.name as sp_name,
(len(c.text) - len(replace(c.text, char(13), ''))) as lines_of_code,
case when o.xtype = 'P' then 'Stored Procedure'
when o.xtype in ('FN', 'IF', 'TF') then 'Function'
end as type_desc
from sysobjects o
inner join syscomments c
on c.id = o.id
where --o.xtype in ('V', 'P', 'FN', 'IF', 'TF', 'TR')
--and
o.category = 0
AND
o.name not in ('fn_diagramobjects', 'sp_alterdiagram', 'sp_creatediagram', 'sp_dropdiagram', 'sp_helpdiagramdefinition', 'sp_helpdiagrams', 'sp_renamediagram', 'sp_upgraddiagrams', 'sysdiagrams')
) t
group by t.sp_name, t.type_desc
order by 1
COMPUTE SUM (sum(t.lines_of_code))
They all give the same results here on several databases. eg 4607 for a SQL Server 2005 SP2 ReportServer database...
Not that I know of, but you could look through the stuff in sysobjects and execute sp_helptext on each proc and view and count the newlines.
If you want a non CTE based solution you could do something like this:
select sum(newlines) from
(
select newlines = (datalength(definition) - datalength(replace(definition, '
', ' '))) / 2 from sys.sql_modules
) as a

Resources