【问题标题】:How to get the query of a VIEW on SQL Server - without CREATE VIEW part如何在 SQL Server 上获取 VIEW 的查询 - 没有 CREATE VIEW 部分
【发布时间】:2021-06-28 14:57:29
【问题描述】:

Microsoftlists 3 种不同的方式来获取视图的定义 在 SQL Server 中(另见 this StackOverflow 问题),f.e.

SELECT definition
FROM sys.sql_modules
WHERE object_id = OBJECT_ID('MY.TEST_VIEW');

但它们都在输出中包含CREATE VIEW(这通常很好),我需要确切的查询,即 for

CREATE VIEW [MY].[TEST_VIEW] (COL /*unfortunately some comment containing `as` and brackets :) */)
AS
   SELECT 1

我只想要

   SELECT 1

虽然总是可以编写一个小的解析器来在视图定义中找到正确的AS,但我更喜欢类似于 Oracle 的 DBA_VIEWS 的东西:

select TEXT from DBA_VIEWS where OWNER = 'MY' and VIEW_NAME = 'TEST_VIEW'

更新 Answer from Dan Guzman 翻译成 vb.net

Imports System.Runtime.Serialization

Imports Microsoft.SqlServer.TransactSql.ScriptDom

<CLSCompliant(False)>
Public Class ViewSelectStatementExtractor : Inherits TSqlFragmentVisitor
    Private SelectStatement As String

    'visitor method invoked for each CREATE VIEW statement in script
    Public Overrides Sub Visit(fragment As CreateViewStatement)

        Dim selectStatementFragment = fragment.SelectStatement
        'extract SELECT statement text from tokens
        Using sw = New IO.StringWriter
            For i = selectStatementFragment.FirstTokenIndex To selectStatementFragment.LastTokenIndex
                sw.Write(selectStatementFragment.ScriptTokenStream(i).Text)
            Next
            Me.SelectStatement = sw.ToString()
        End Using
    End Sub

    'Get view SELECT statement Using T-SQL script dom parser and visitor
    Shared Function Extract(createViewStatement As String) As String
        Dim parser = New Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser(True)
        Dim parseErrors As IList(Of ParseError) = New List(Of ParseError)
        Dim fragment As TSqlFragment
        Using stringReader = New IO.StringReader(createViewStatement)
            fragment = parser.Parse(stringReader, parseErrors)
        End Using
        If (parseErrors.Count > 0) Then
            Dim errorStrings = From e In parseErrors Select $"{e.Line},{e.Column}: {e.Message}"
            Throw New ParseTSqlException($"{parseErrors.Count} parsing errors: {vbNewLine}{String.Join(vbNewLine, errorStrings)}")
        End If

        Dim visitor = New ViewSelectStatementExtractor
        fragment.Accept(visitor)
        Return visitor.SelectStatement
    End Function
End Class

<Serializable>
Public Class ParseTSqlException
    Inherits Exception

    Public Sub New()
    End Sub

    Public Sub New(message As String)
        MyBase.New(message)
    End Sub

    Public Sub New(message As String, innerException As Exception)
        MyBase.New(message, innerException)
    End Sub

    Protected Sub New(info As SerializationInfo, context As StreamingContext)
        MyBase.New(info, context)
    End Sub
End Class

【问题讨论】:

  • 我相信您可以检索到的所有定义都包括完整的定义。
  • 您可以使用 T-SQL 脚本 DOM 从视图中提取 select 语句。
  • @DanGuzman 你的意思是来自 Microsoft.SqlServer.TransactSql.ScriptDom 的 TSqlParser.Parse 吗? (docs.microsoft.com/en-us/dotnet/api/…) 我想这会让我免于自己编写“评论解析器”,但代价是额外的依赖。

标签: sql-server


【解决方案1】:

虽然您可以使用 T-SQL 从 CREATE VIEW 定义中提取 SELECT 查询,但当视图定义包含 cmets 和其他意外构造时,该方法可能不可靠。考虑改用 T-SQL 脚本 DOM 库。

下面是一个 .NET-ish PowerShell 脚本示例,它使用访问者执行从 this article 收集的任务,您可以根据需要进行扩展。这使用本地 SSMS 安装中的 Microsoft.SqlServer.TransactSql.ScriptDom.dll 程序集,但您可能希望使用 DacFx NuGet package 中的程序集来避免 SSMS 依赖性并提供对特定版本的更多控制。

由于 PowerShell 访问者类实现派生自外部 Microsoft.SqlServer.TransactSql.ScriptDom.dll 程序集中定义的类型,因此必须首先将程序集加载到应用程序域中,以便 PS 访问者类可以编译。我使用包装脚本来加载类型并使用dot source 主脚本来解决此问题。

Extract-ViewSelectStatementWrapper.ps1:

Add-Type -Path "C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Extensions\Application\Microsoft.SqlServer.TransactSql.ScriptDom.dll"
. "C:\Users\dguzman.GUZMAN1\Documents\Powershell\Extract-ViewSelectStatement.ps1"

Extract-ViewSelectStatement.ps1:

class ViewVisitor: Microsoft.SqlServer.TransactSql.ScriptDom.TSqlFragmentVisitor {

    [String]$SelectStatement

    # visitor method invoked for each CREATE VIEW statement in script
    [void]Visit ([Microsoft.SqlServer.TransactSql.ScriptDom.CreateViewStatement] $fragment) {

        $selectStatementFragment = $fragment.SelectStatement
        # extract SELECT statement text from tokens
        $sw = New-Object System.IO.StringWriter
        for($i = $selectStatementFragment.FirstTokenIndex; $i -le $selectStatementFragment.LastTokenIndex; ++$i) {
            $sw.Write($selectStatementFragment.ScriptTokenStream[$i].Text)
        }
        $this.SelectStatement = $sw.ToString()
        $sw.Close()

    }

    # get view select statement using T-SQL script dom parser and visitor
    static [String] getSelectStatement($createViewStatement) {

        $parser = New-Object Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser($true)
        $parseErrors = New-Object System.Collections.Generic.List[Microsoft.SqlServer.TransactSql.ScriptDom.ParseError]
        $stringReader = New-Object System.IO.StringReader($createViewStatement)

        $fragment = $parser.Parse($stringReader, [ref]$parseErrors)
        if($parseErrors.Count -gt 0) {
            throw "$($parseErrors.Count) parsing errors: $(($parseErrors | ConvertTo-Json))"
        }

        $visitor = [ViewVisitor]::new()

        $fragment.Accept($visitor)

        return $visitor.SelectStatement

    }
}

Function Get-ViewSelectStatement ($connectionString, $viewNAme) {

    $query = @"
    SELECT definition
    FROM sys.sql_modules
    WHERE object_id = OBJECT_ID(@ViewName);
"@

    $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
    $connection.Open()
    $command = New-Object System.Data.SqlClient.SqlCommand($query, $connection)
    [void]$command.Parameters.Add("@ViewName", [System.Data.SqlDbType]::NVarChar, 261)
    $command.Parameters["@ViewName"].Value = $viewName
    $createViewStatement = $command.ExecuteScalar()
    $connection.Close()

    return [ViewVisitor]::getSelectStatement($createViewStatement)


}


# ############
# ### MAIN ###
# ############

$connectionString = "Data Source=YourServer;Initial Catalog=YourDatabase;Integrated Security=SSPI"

try {

    Get-ViewSelectStatement -connectionString $connectionString -viewName "MY.TEST_VIEW"

}
catch {

    throw

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 2013-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    相关资源
    最近更新 更多