Sql server 在现有过程中定位旧式联接
我正在将二十多个数据库迁移和重构到2019年。它们都至少包含一些使用旧的非ANSI联接的存储过程。总共有4800多个过程,所以我不想手动查看它们以查找旧式联接。更复杂的是,许多旧连接都嵌入到动态sql中,sql升级顾问无法捕捉到这一点 我想一定有办法查询sys.sql_模块来识别它们,但想不出要为definition列上的条件编写什么。有人能为这个项目提供建议吗???下面,还是提供不同的方法Sql server 在现有过程中定位旧式联接,sql-server,Sql Server,我正在将二十多个数据库迁移和重构到2019年。它们都至少包含一些使用旧的非ANSI联接的存储过程。总共有4800多个过程,所以我不想手动查看它们以查找旧式联接。更复杂的是,许多旧连接都嵌入到动态sql中,sql升级顾问无法捕捉到这一点 我想一定有办法查询sys.sql_模块来识别它们,但想不出要为definition列上的条件编写什么。有人能为这个项目提供建议吗???下面,还是提供不同的方法 ``` SELECT objects.object_id , schemas.name A
```
SELECT
objects.object_id
, schemas.name AS [Schema]
, objects.name AS Object_Name
, objects.type_desc AS [Type]
, sql_modules.definition AS Definition
FROM
sys.sql_modules sql_modules
INNER JOIN sys.objects objects
ON sql_modules.object_id = objects.object_id
INNER JOIN sys.schemas schemas
ON objects.schema_id = schemas.schema_id
WHERE
sql_modules.definition COLLATE SQL_Latin1_General_CP1_CI_AS LIKE '%??????????????????%' ESCAPE '\'
AND objects.type_desc = 'SQL_STORED_PROCEDURE';
```
我不确定仅仅通过查询定义就可以隐式地识别非ANSI存储过程,但是您可以缩小必须查看的数量。我刚刚在一个大约有430个存储过程的数据库上进行了测试,我知道这里没有非ANSI连接。结果有40个假阳性。当然比浏览所有430条更好:
SELECT
objects.object_id
, schemas.name AS [Schema]
, objects.name AS Object_Name
, objects.type_desc AS [Type]
, sql_modules.definition AS Definition
FROM
sys.sql_modules sql_modules
INNER JOIN sys.objects objects
ON sql_modules.object_id = objects.object_id
INNER JOIN sys.schemas schemas
ON objects.schema_id = schemas.schema_id
-- Pick up only those statements that have 'FROM' in the definition
WHERE sql_modules.definition COLLATE SQL_Latin1_General_CP1_CI_AS LIKE '%FROM%'
-- Exclude any statements that have 'JOIN' in the definition
AND sql_modules.definition COLLATE SQL_Latin1_General_CP1_CI_AS NOT LIKE '%JOIN%'
-- Exclude any where there is no comma found after the FROM statement
AND CHARINDEX(
',',
SUBSTRING(
sql_modules.definition,
CHARINDEX(
'FROM',
sql_modules.definition
),
LEN(sql_modules.definition)
)
) > 0
AND objects.type_desc = 'SQL_STORED_PROCEDURE';
使用
--一种虚拟过程,用于测试
create procedure testxyz
as
begin
exec('
select *
from tableA as a, tableB as b
where a.id = b.columnid
')
exec('
select *
from X as x
join Y as y on x.col1 = y.col2
where x.id = y.columnid
')
exec('
select *
from (select * from X) as x
join (select * from Y) as y on x.col1 = y.col2
where x.id = y.columnid
')
exec('
select *
from (select * from a, b where a.foo = b.bar) as x
join (select * from Y) as y on x.col1 = y.col2
where x.id = y.columnid
');
select *
from (select * from a, b where a.foo = b.bar) as x
join (select * from Y) as y on x.col1 = y.col2
where x.id = y.columnid;
exec sp_executesql N'select * from k, l where k.foo = l.bar '
declare @sql nvarchar(max) = N'select * from o,p where o.id = p.id';
exec sp_executesql @sql
declare @sql1 nvarchar(max) = N'select * from r,s where r.id = s.id';
exec(@sql1)
--constructed dynamic sql ....
declare @sql2 nvarchar(max) = N'select * from ';
select @sql2 = @sql2 + 'pr,qr where pr.id = qr.id';
exec(@sql2)
update tableX
set col1 = 123
from tableX, TableY
where TableX.id = TableY.id
delete a
from tableAB as a, tableCD as b
where a.id = b.id
insert into #temp(x, y, z)
select a, b, c
from tableX, tableY
where idX=idY
end
……以下
--slow...better execute this per proc, or for iterate through batches of procs/modules
select top (50)
object_name(m.object_id) as modulename,
sfc.f.value('data((./../../*[local-name() = ("SqlQuerySpecification", "SqlUpdateSpecification", "SqlDeleteSpecification")]/comment())[1])', 'nvarchar(max)') as thequery,
sfc.f.query('data(comment()[1])') as fromclause,
stmt.isdynamicsql
from sys.all_sql_modules as m
cross apply (select cast(dbo.parseSqlToXml(m.definition) as xml) as sqlparse) as prs
cross apply
(
--normal sql
select prs.sqlparse as thesql, 0 as isdynamicsql
union all
select
--dynamic (exec/sp_executesql) sql in token_string
cast(dbo.parseSqlToXml(stuff(reverse(stuff(reverse(replace(replace('xN'+rtrim(ltrim(replace(replace(t.s.value('.', 'nvarchar(max)'), '\r\n', ' '), '\t', ' '))), 'xNN', 'xN'), 'XN', '')), 1, 1, '')), 1, 1, '')) as xml) as thesql, 1 as isdynamicsql
from prs.sqlparse.nodes('SqlScript/SqlBatch/Tokens/Token[@type="TOKEN_STRING"][contains(lower-case(.), "from")]') as t(s)
) as stmt
--..each fromclause in thesql
cross apply stmt.thesql.nodes('//SqlFromClause[count(SqlDerivedTableExpression) + count(SqlTableRefExpression) > 1]') as sfc(f)
order by m.object_id desc;
…返回:
+--------------------------+-----------------------------------+------------------------------------------+--------------+
| modulename | thequery | fromclause | isdynamicsql |
+--------------------------+-----------------------------------+------------------------------------------+--------------+
| testxyz | select * from a, b where a.foo... | from a, b | 0 |
| testxyz | update tableX set col1 = 123 f... | from tableX, TableY | 0 |
| testxyz | delete a from tableAB as a, ta... | from tableAB as a, tableCD as b | 0 |
| testxyz | select a, b, c from tableX, ta... | from tableX, tableY | 0 |
| testxyz | select * from tableA as a, tab... | from tableA as a, tableB as b | 1 |
| testxyz | select * from a, b where a.foo... | from a, b | 1 |
| testxyz | select * from k, l where k.foo... | from k, l | 1 |
| testxyz | select * from o,p where o.id =... | from o,p | 1 |
| testxyz | select * from r,s where r.id =... | from r,s | 1 |
| sp_MShelpfulltextscript | select @catname = f.name from ... | from dbo.sysfulltextcatalogs f, dbo.syso | 0 |
| sp_MSgetalternaterecgens | select distinct p.pubid as alt... | from dbo.sysmergesubscriptions s, dbo.sy | 0 |
| sp_check_sync_trigger | select @trigid = so1.object_id... | from sys.objects so1, sys.objects so2 | 0 |
+--------------------------+-----------------------------------+------------------------------------------+--------------+
并非所有构造的sql都被正确地“解析”
或者,扩展事件可以捕获不推荐使用的功能。大多数过程都是在执行多个update语句之前插入到临时表或表变量中的,因此几乎所有过程的FROM后面都有逗号。到目前为止,我还发现一些在SQL的非动态部分使用ANSI连接,但在动态部分使用旧连接(因为当然有)。我一直试图避免它,但我担心我可能需要检查FROM后面是否有逗号,除非找到更新、顺序、组或SELECT。这需要某种whilenotdone类型的循环,因为其中一些语句中有10+FROM子句。不过感谢您的快速回复。我把它添加到并编辑了,但为时已晚:)我喜欢2003年的遗留应用程序。那时我真的没有答案,这听起来像是一个相当痛苦的退役工作。祝你好运。我将把答案留在这里,以防将来对任何搜索者都有好处。