Sql server 在现有过程中定位旧式联接

Sql server 在现有过程中定位旧式联接,sql-server,Sql Server,我正在将二十多个数据库迁移和重构到2019年。它们都至少包含一些使用旧的非ANSI联接的存储过程。总共有4800多个过程,所以我不想手动查看它们以查找旧式联接。更复杂的是,许多旧连接都嵌入到动态sql中,sql升级顾问无法捕捉到这一点 我想一定有办法查询sys.sql_模块来识别它们,但想不出要为definition列上的条件编写什么。有人能为这个项目提供建议吗???下面,还是提供不同的方法 ``` SELECT objects.object_id , schemas.name A

我正在将二十多个数据库迁移和重构到2019年。它们都至少包含一些使用旧的非ANSI联接的存储过程。总共有4800多个过程,所以我不想手动查看它们以查找旧式联接。更复杂的是,许多旧连接都嵌入到动态sql中,sql升级顾问无法捕捉到这一点

我想一定有办法查询sys.sql_模块来识别它们,但想不出要为definition列上的条件编写什么。有人能为这个项目提供建议吗???下面,还是提供不同的方法

```
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年的遗留应用程序。那时我真的没有答案,这听起来像是一个相当痛苦的退役工作。祝你好运。我将把答案留在这里,以防将来对任何搜索者都有好处。