我想获取所有插入到表中而不指定列名的存储过程的列表。
我通过使用以下存储过程获得的最接近的信息:
CREATE PROCEDURE [dbo].[CheckSQLModules]
AS
DECLARE m CURSOR FOR
SELECT '[' + s.name + ']' + '.[' + o.name + ']' AS name, o.type_desc
FROM sys.sql_modules m
INNER JOIN sys.objects o ON m.object_id = o.object_id
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE m.is_schema_bound = 0
DECLARE @name sysname, @type_desc sysname
OPEN m
FETCH NEXT FROM m into @name, @type_desc
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
EXEC SP_REFRESHSQLMODULE @name
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK
PRINT ERROR_MESSAGE()
PRINT @type_desc + ': ' + @name
PRINT ''
END CATCH
FETCH NEXT FROM m INTO @name, @type_desc
END
CLOSE m
DEALLOCATE m
GO
但是我不确定结果是否100%正确。
答案 0 :(得分:0)
通常,在问这样的问题时,最好包括一些到目前为止您尝试过的细节。无论如何,这应该可以帮助您入门。
SELECT
ObjectName = obj.Nm,
Preview = SUBSTRING(def.Txt, srch.Idx, 100)+'...',
FullDef = SUBSTRING(def.Txt, 1, 100)
FROM INFORMATION_SCHEMA.ROUTINES AS r
CROSS APPLY (VALUES(r.SPECIFIC_SCHEMA+'.'+r.SPECIFIC_NAME)) AS obj(Nm)
CROSS APPLY (VALUES(OBJECT_DEFINITION(OBJECT_ID(obj.Nm)))) AS def(Txt)
CROSS APPLY (VALUES(CHARINDEX('INSERT ',def.Txt))) AS srch(Idx)
WHERE r.ROUTINE_TYPE = 'PROCEDURE'
AND srch.Idx > 0;
返回的内容(为简洁起见被删节):
ObjectName Preview FullDef
------------------------------------ ----------------------------------- -----------------------------------
metadata.usp_objectReadmeSections INSERT metadata.objectReadmeSe... CREATE PROC metadata.usp_objec...
dbo.usp_BuildCalendarAB INSERT dbo.CalendarAB SELECT d... CREATE PROC dbo.usp_BuildCalen...
metadata.usp_diskUsageByDB INSERT ##dbSizeInfo... CREATE PROC metadata.usp_diskU...
这将返回任何包含INSERT语句的存储过程的详细信息。我在这里不区分要插入哪种表(临时表,表变量或常规表)。可以,但是可以根据需要添加其他过滤器。
答案 1 :(得分:0)
您可以使用正则表达式分析代码以查看是否存在类似的模式
插入表名选择
插入表名值
由于TSQL不支持正则表达式,因此您可以编写CLR存储过程,也可以针对本地代码库运行该过程。
下面是检查这些情况的样本正则表达式。您必须启用忽略大小写并使用扩展的正则表达式。
INSERT\s+[a-zA-Z0-9$_]+\s+(SELECT|VALUES)
答案 2 :(得分:0)
不使用脚本DOM来解析T-SQL并不是一件容易的事,因为语义相同的语言构造有多种变体。尝试使用字符串搜索这样做最多是脆弱的,因为很难确定文本是否包含在可执行代码,注释,多行中等之间。
考虑使用Microsoft.SqlServer.TransactSql.ScriptDom
中可用的official Microsoft SQL Server Data-Tier Application Framework NuGet package程序集。可以在.NET中以编程方式使用此程序集来满足临时需要,例如您的代码分析规则。下面是一个PowerShell示例,该示例分析指定数据库中的所有存储过程,并列出具有INSERT
语句而没有显式列列表的存储过程。
# add script dom assembly type, downloading if needed
Function Add-TSqlScriptDomType() {
$dacFxNuGetUrl = "https://www.nuget.org/api/v2/package/Microsoft.SqlServer.DacFx.x64"
$scriptDomAssemblyPath = "$PSScriptRoot\Microsoft.SqlServer.TransactSql.ScriptDom.dll"
if(![System.IO.File]::Exists($scriptDomAssemblyPath)) {
# assembly doesn't exist in this script folder, download latest DacFx package from NuGet and extract the T-SQL Script DOM assembly here
#download DacFx NuGet package containing assembly
$response = Invoke-WebRequest -Uri $dacFxNuGetUrl
if ($response.StatusCode -ne 200) {
throw "Unable to download Microsoft.SqlServer.TransactSql.ScriptDom NuGet package: $($response.StatusCode) : $($response.StatusDescription)"
}
# decompress NuGet package to temp folder
$tempZipFilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName() + ".zip")
[System.IO.File]::WriteAllBytes($tempZipFilePath, $response.Content)
$response.BaseResponse.Dispose()
$tempUnzipFolderPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
Expand-Archive -Path $tempZipFilePath -DestinationPath $tempUnzipFolderPath
$tempZipFilePath | Remove-Item
# copy Microsoft.SqlServer.TransactSql.ScriptDom.dll assembly and remove temp files
Copy-Item "$tempUnzipFolderPath\lib\net*\Microsoft.SqlServer.TransactSql.ScriptDom.dll" $scriptDomAssemblyPath
$tempUnzipFolderPath | Remove-Item -Recurse
}
Add-Type -Path $scriptDomAssemblyPath
}
# return count of insert statements in script without explict column list
Function Get-InsertStatementCountWithoutColumnList($script) {
$insertStatementCountWithoutColumnList = 0
# use the appropriate TSqlParser version for the target SQL Server version
$parser = New-Object Microsoft.SqlServer.TransactSql.ScriptDom.TSql140Parser($true)
$parseErrors = New-Object System.Collections.Generic.List[Microsoft.SqlServer.TransactSql.ScriptDom.ParseError]
$scriptReader = New-Object System.IO.StringReader($script)
$fragment = $parser.Parse($scriptReader, [ref]$parseErrors)
$scriptChanged = $false
if($parseErrors.Count -eq 0) {
foreach($statement in $fragment.Batches[0].Statements[0].StatementList.Statements) {
switch($statement.GetType().ToString())
{
"Microsoft.SqlServer.TransactSql.ScriptDom.InsertStatement" {
# $statement.InsertSpecification.Columns
if($statement.InsertSpecification.Columns.Count -eq 0)
{
$insertStatementCountWithoutColumnList += 1
}
break
}
}
}
}
else {
throw "Error(s) parsing script"
}
return $insertStatementCountWithoutColumnList
}
############
### main ###
############
try {
Add-TSqlScriptDomType
$connectionString = "Data Source=.;Initial Catalog=YourDatabase;Integrated Security=SSPI";
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString);
$query = @"
SELECT QUOTENAME(OBJECT_SCHEMA_NAME(p.object_id)) + '.' + QUOTENAME(p.name) AS procedure_name, sm.definition
FROM sys.procedures AS p
JOIN sys.sql_modules AS sm ON sm.object_id = p.object_id;
"@
$selectCommand = New-Object System.Data.SqlClient.SqlCommand($query, $connection)
$connection.Open();
$reader = $selectCommand.ExecuteReader()
while($reader.Read()) {
$insertStatementCountWithoutColumnList = Get-InsertStatementCountWithoutColumnList -script "$($reader["definition"])"
if($insertStatementCountWithoutColumnList -gt 0) {
Write-Host "Procedure $($reader["procedure_name"]) has $insertStatementCountWithoutColumnList insert statements without a column list"
}
}
}
catch {
throw
}
请注意,此示例下载了NuGet程序包,并将程序集提取到当前脚本文件夹中,并将该位置用于Add-Type
引用。如果配置了NuGet存储库,则可以改用NuGet程序包管理器。
答案 3 :(得分:0)
按照 Alan Burstein 的建议,我设法找到了这个解决方案:
CREATE FUNCTION dbo.ReplaceSpace(@txt varchar(MAX)) RETURNS varchar(MAX)
AS
BEGIN
RETURN replace(replace(replace(replace(@txt, char(13), ''), char(10), ''), char(9), ''), ' ', '')
END
GO DROP TABLE IF EXISTS # t
GO
DECLARE @tb_name varchar(100) = 'table_name', @len int
SELECT
@len = len(@tb_name)
SELECT
@len = @len + 1
SELECT DISTINCT
object_id INTO # t
FROM
sys.procedures
WHERE
object_definition(object_id) like '%' + @tb_name + '%' CREATE nonclustered INDEX # t_i1
ON # t(object_id)
DELETE
# t
WHERE
object_id not in
(
SELECT DISTINCT
t2.[object_id] ProcedureName
FROM
sys.sql_dependencies t1
INNER JOIN
sys.procedures t2
ON t1.object_id = t2.object_id
INNER JOIN
sys.objects t3
ON t1.referenced_major_id = t3.object_id
WHERE
t3.[name] = @tb_name
)
SELECT
ObjectName = obj.Nm,
Preview = SUBSTRING(def.Txt, srch.Idx, 100) + '...',
Preview2 = dbo.ReplaceSpace(replace(replace(replace(SUBSTRING(def.Txt, srch.Idx, 100), 'insert', ''), 'into', ''), 'dbo.', '')),
Preview3 = substring(dbo.ReplaceSpace(replace(replace(replace(SUBSTRING(def.Txt, srch.Idx, 100), 'insert', ''), 'into', ''), 'dbo.', '')), @len, 1),
FullDef = SUBSTRING(def.Txt, 1, 100)
FROM
INFORMATION_SCHEMA.ROUTINES AS r CROSS APPLY (
VALUES
(
r.SPECIFIC_SCHEMA + '.' + r.SPECIFIC_NAME
)
) AS obj(Nm) CROSS APPLY (
VALUES
(
OBJECT_DEFINITION(OBJECT_ID(obj.Nm))
)
) AS def(Txt) CROSS APPLY (
VALUES
(
CHARINDEX('INSERT ', def.Txt)
)
) AS srch(Idx)
WHERE
r.ROUTINE_TYPE = 'PROCEDURE'
AND dbo.ReplaceSpace(replace(replace(replace(SUBSTRING(def.Txt, srch.Idx, 100), 'insert', ''), 'into', ''), 'dbo.', '')) like @tb_name + '%'
AND srch.Idx > 0
AND object_id(obj.nm) in
(
SELECT
object_id
FROM
# t
)
AND substring(dbo.ReplaceSpace(replace(replace(replace(SUBSTRING(def.Txt, srch.Idx, 100), 'insert', ''), 'into', ''), 'dbo.', '')), 16, 1) <> '('