SQL Server将标识符传递给存储过程/动态SQL 背景:

SQL Server将标识符传递给存储过程/动态SQL 背景:,sql,sql-server,tsql,ssms,Sql,Sql Server,Tsql,Ssms,SQL Server Management Studio允许定义自己的查询快捷方式(工具>选项>环境>键盘>查询快捷方式): 图片来源: 它工作正常,但它以基本形式连接查询(据我所知,仅在最后)。查询: 尝试#1 现在我想写一些更具体的东西,例如将表名传递/连接到以下查询(这只是一个示例): 因此,当我在查询快捷方式中写入时: SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(' 我必须使用: my_schema.my_tabl

SQL Server Management Studio
允许定义自己的查询快捷方式(
工具>选项>环境>键盘>查询快捷方式
):

图片来源:

它工作正常,但它以基本形式连接查询(据我所知,仅在最后)。查询:


尝试#1 现在我想写一些更具体的东西,例如将表名传递/连接到以下查询(这只是一个示例):

因此,当我在查询快捷方式中写入时:

SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID('
我必须使用:

my_schema.my_table')
-- highlight it
-- press CTRL + 3
附加的
”)
非常难看和不方便。

尝试#2: 第二次尝试是使用动态SQL:

EXEC dbo.sp_executesql
      N'SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(@obj_name)'
     ,N'@obj_name SYSNAME'
     ,
EXEC dbo.sp_executesql
      N'SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(@obj_name)'
     ,N'@obj_name SYSNAME'
     ,
执行:

 my_table
 -- highligt it 
 -- and run 
my_schema.my_table
[my_schema].[my_table]

当引用表名时也有效
[my\u table]
。只要对象位于
dbo
(默认)模式中

问题是,当表具有架构时,它将无法工作:

执行:

 my_table
 -- highligt it 
 -- and run 
my_schema.my_table
[my_schema].[my_table]

“.”附近的语法不正确

我当然可以写:

EXEC dbo.sp_executesql
      N'SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(@obj_name)'
     ,N'@obj_name SYSNAME'
     ,'
并称之为:

 [my_schema].[my_table]'
但是附加的
也很难看,也很不方便。

问题: >P>是否可以传递值,查询快捷方式窗口,中间(位置或甚至不止一个值)?

  • 是否可以传递do存储过程/动态sql限定标识符,而不使用
    '

  • 评论:
    • 我不搜索SSM的插件
    • 我不想将对象名称包装为“我的模式.我的表”
    • 我知道有
      sp_helptext
      (这只是一个例子,我搜索方法)
    • 第一个问题是特定于工具的(我知道),但第二个问题是关于
      SQLServer
    编辑:

    要澄清在不使用
    的情况下将标识符传递给SP,请执行以下操作:

    1。是否可以传递值,在中间查询快捷方式窗口? 据我所知,没有解决办法来实现这一点

    1-b。是否可以传递多个值? 可以使用分隔符对字符串值执行此操作,然后在另一侧拆分值。遗憾的是,没有多少特殊字符来完成这项工作,因为它们几乎都会引发语法错误。然而,“#”可能是一个明智的选择,因为它已经是tempDB中临时表SQL的一个特殊字符。只要检查您是否还没有使用它的标识符,因为它是SQL允许的(很难,它被禁止作为第一个字符)

    下面是一个例子:
    创建一个存储过程以将参数接收到一个字符串中,并将该字符串拆分为每个参数

        CREATE PROCEDURE sp_PassingMultipleStringValues 
            @Param1 NVARCHAR(MAX)
        AS
        
        --Here I'm using a XML split, but feel free to use any string split function you already have.
        DECLARE @xml        AS XML,
                @separator  AS VARCHAR(1)
        
        SELECT  @separator ='#',
                @xml = CAST('<X>'+ (REPLACE(@Param1,@separator ,'</X><X>') +'</X>') AS XML)
        
        SELECT N.value('.', 'VARCHAR(200)') AS value 
        FROM @xml.nodes('X') as T(N)
        --Do whatever is needed with them
    
    如果您确实希望使用相同的标识符处理两个不同的模式,那么仍然可以使用答案1-b中解释的方法将模式和标识符作为两个参数传递

    一切都在一个例子中 因为这里我们要传递多个标识符并指定它们的模式,所以需要两个分隔符

    CREATE PROCEDURE sp_MultiArgsWithSchema
        @Param1 NVARCHAR(MAX)
    AS
    
    SELECT @Param1 = REPLACE(REPLACE(@Param1, '[', ''), ']', '')
    
    --Here I'm using a XML split, but feel free to use any string split function you already have.
    DECLARE @xml            AS XML,
            @ArgSeparator   AS VARCHAR(2),
            @SchemaSeparor  AS VARCHAR(1)
    
    SELECT  @ArgSeparator = '##',
            @SchemaSeparor = '#',
            @xml = CAST('<X>'+ (REPLACE(@Param1,@ArgSeparator, '</X><X>') +'</X>') AS XML)
    
    IF OBJECT_ID('tempdb..#QualifiedIdentifiers') IS NOT NULL 
        DROP TABLE #QualifiedIdentifiers;
    
    --While splitting, we are putting back the dot instead of '#' between schema and name of object
    SELECT QualifiedIdentifier = REPLACE(N.value('.', 'VARCHAR(200)'), @SchemaSeparor, '.') 
    INTO #QualifiedIdentifiers
    FROM @xml.nodes('X') as T(N)
    
    SELECT * FROM #QualifiedIdentifiers
    
    --From here, use what is inside #QualifiedIdentifiers and Dynamic SQL if need to achieve what is needed
    DECLARE @QualifiedIdentifier    NVARCHAR(500)
    WHILE EXISTS(SELECT TOP 1 1 FROM #QualifiedIdentifiers)
    BEGIN
        SELECT TOP 1 @QualifiedIdentifier = QualifiedIdentifier
        FROM #QualifiedIdentifiers
    
        SELECT * 
        FROM sys.columns 
        WHERE [object_id] = OBJECT_ID(@QualifiedIdentifier)
     
        DELETE TOP (1) 
        FROM #QualifiedIdentifiers
        WHERE QualifiedIdentifier = @QualifiedIdentifier
    END
    
    创建过程sp_MultiArgsWithSchema
    @参数1 NVARCHAR(最大值)
    作为
    选择@Param1=REPLACE(REPLACE(@Param1,“[”,”,“],”)
    --这里我使用的是XML拆分,但是可以随意使用您已有的任何字符串拆分函数。
    将@xml声明为xml,
    @ArgSeparator作为VARCHAR(2),
    @SchemaSeparor作为VARCHAR(1)
    选择@ArgSeparator='##',
    @SchemaSeparor='#',
    @xml=CAST(“”+(将(@Param1,@ArgSeparator,)+“”)替换为xml)
    如果对象ID('tempdb..#QualifiedIdentifiers')不为空
    删除表格#限定标识符;
    --拆分时,我们将在模式和对象名称之间放回点而不是“#”
    选择QualifiedIdentifier=REPLACE(N.value(‘.,‘VARCHAR(200)’,@SchemaSeparor,’)
    进入资格鉴定人
    从@xml.nodes('X')作为T(N)
    从限定标识符中选择*
    --从这里开始,如果需要实现所需的功能,请使用#限定标识符和动态SQL
    声明@QualifiedIdentifier NVARCHAR(500)
    存在时(从#限定标识符中选择前1名)
    开始
    选择TOP 1@QualifiedIdentifier=QualifiedIdentifier
    来自资格鉴定人
    选择*
    从sys.columns
    其中[object\u id]=object\u id(@QualifiedIdentifier)
    删除顶部(1)
    来自资格鉴定人
    其中QualifiedIdentifier=@QualifiedIdentifier
    结束
    
    用法(请注意,指定架构不是强制性的):


    因此,由于必须将拆分字符加倍是不方便的,因此最好能像上面所述那样猜测模式。

    在传递多部分标识符时不必用引号括起来

    解决方案:

  • 查询快捷方式将在数据库中创建一个具有特定名称的同义词,并截取该特定同义词的创建

    在查询快捷方式中设置以下快捷方式。(确保包含最后一个空格)

    正如@Vladimir所建议的,这里我们使用“sp_executesql”能够同时创建触发器和同义词

    这是触发器的代码,没有内联

    CREATE TRIGGER DDLTrigger_QueryShortcutX ON DATABASE FOR CREATE_SYNONYM
    AS
    BEGIN
        DECLARE @EventData      XML = EVENTDATA(),
                @SynonymName    NVARCHAR(255),
                @DbName         NVARCHAR(255),
                @SchemaName     NVARCHAR(255),
                @ObjectName     NVARCHAR(255),
                @Alias          NVARCHAR(255)
    
        SELECT  @SynonymName    = @EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)') 
    
        --Safety in case someone else really create a synonym meanwhile. 
        IF(@SynonymName = 'QueryShortcutX')
        BEGIN
    
            --2. Clean up what we created
            DROP SYNONYM QueryShortcutX
            DROP TRIGGER DDLTrigger_QueryShortcutX ON DATABASE
    
            --3. Parsing identifier code here
            SELECT  @DbName         = @EventData.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'NVARCHAR(255)'), 
                    @SchemaName     = @EventData.value('(/EVENT_INSTANCE/TargetSchemaName)[1]', 'NVARCHAR(255)'), 
                    @ObjectName     = @EventData.value('(/EVENT_INSTANCE/TargetObjectName)[1]', 'NVARCHAR(255)'),
                    @Alias          = (CASE WHEN LEN(@SchemaName) > 0 THEN @SchemaName + '.' ELSE '' END) + @ObjectName
    
            --4. Here, write any print/select statement you want. 
            --For maintenance, it would be easier to just call a stored procedure from here with parameter and put the desired print/select there. 
            --Thus avoiding to redo inlining the whole trigger each time.
            --EXEC yourStoredProcHere @Param = @Alias
            SELECT  DbName      = @DbName,
                    SchemaName  = @SchemaName,
                    ObjectName  = @ObjectName,
                    Alias       = @Alias,
                    ObjectId    = OBJECT_ID(@Alias)
    
        END
    END
    
    这是没有内联的快捷方式的代码

        DECLARE @CreateTriggerSQL NVARCHAR(MAX) = 'Trigger creation code here...'
        IF EXISTS(SELECT TOP 1 1 FROM sys.triggers WHERE name = 'DDLTrigger_QueryShortcutX') 
        BEGIN 
            DROP TRIGGER DDLTrigger_QueryShortcutX ON DATABASE 
        END 
    
        EXEC sp_executeSQL @CreateTriggerSQL 
    
        IF EXISTS(SELECT TOP 1 1 FROM sys.synonyms WHERE name = 'QueryShortcutX') 
        BEGIN 
            DROP SYNONYM QueryShortcutX 
        END 
        CREATE SYNONYM QueryShortcutX FOR 
    
  • 触发器本身和同义词都会删除,以避免模式污染

  • 触发器解析信息以检索标识符
  • 根据您的需要使用标识符。(如果需要,请使用动态SQL)

  • 每个测试项目的结果

    1.RealColumnName    
    2.WhatEverText
    3.dbo.tests
    4.[No selection]
    5.dbo.tests.very.much
    
     DbName SchemaName      ObjectName      Alias           ObjectId
    1.TEST                  RealColumnName  RealColumnName  NULL --FN OBJECT_ID doesn't return value with only column name
    2.TEST                  WhatEverText    WhatEverText    NULL
    3.TEST  dbo             tests           dbo.tests       245575913
    4.Incorrect syntax near 'FOR'.
    5.TEST  very            much            very.much       NULL
    
    我所做的解析无法正确处理包含两个以上多部分的标识符。如果你想改进它。下面的XML显示了要使用的标记

    <TargetServerName>dbo</TargetServerName>
    <TargetDatabaseName>tests</TargetDatabaseName>
    <TargetSchemaName>very</TargetSchemaName>
    <TargetObjectName>much</TargetObjectName>
    
    dbo
    测验
    非常
    很
    

    注意:

    • 如果愿意,可以让触发器永久保留在数据库中
    • 还有,如果你想通过mult
      CREATE TRIGGER DDLTrigger_QueryShortcutX ON DATABASE FOR CREATE_SYNONYM
      AS
      BEGIN
          DECLARE @EventData      XML = EVENTDATA(),
                  @SynonymName    NVARCHAR(255),
                  @DbName         NVARCHAR(255),
                  @SchemaName     NVARCHAR(255),
                  @ObjectName     NVARCHAR(255),
                  @Alias          NVARCHAR(255)
      
          SELECT  @SynonymName    = @EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)') 
      
          --Safety in case someone else really create a synonym meanwhile. 
          IF(@SynonymName = 'QueryShortcutX')
          BEGIN
      
              --2. Clean up what we created
              DROP SYNONYM QueryShortcutX
              DROP TRIGGER DDLTrigger_QueryShortcutX ON DATABASE
      
              --3. Parsing identifier code here
              SELECT  @DbName         = @EventData.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'NVARCHAR(255)'), 
                      @SchemaName     = @EventData.value('(/EVENT_INSTANCE/TargetSchemaName)[1]', 'NVARCHAR(255)'), 
                      @ObjectName     = @EventData.value('(/EVENT_INSTANCE/TargetObjectName)[1]', 'NVARCHAR(255)'),
                      @Alias          = (CASE WHEN LEN(@SchemaName) > 0 THEN @SchemaName + '.' ELSE '' END) + @ObjectName
      
              --4. Here, write any print/select statement you want. 
              --For maintenance, it would be easier to just call a stored procedure from here with parameter and put the desired print/select there. 
              --Thus avoiding to redo inlining the whole trigger each time.
              --EXEC yourStoredProcHere @Param = @Alias
              SELECT  DbName      = @DbName,
                      SchemaName  = @SchemaName,
                      ObjectName  = @ObjectName,
                      Alias       = @Alias,
                      ObjectId    = OBJECT_ID(@Alias)
      
          END
      END
      
          DECLARE @CreateTriggerSQL NVARCHAR(MAX) = 'Trigger creation code here...'
          IF EXISTS(SELECT TOP 1 1 FROM sys.triggers WHERE name = 'DDLTrigger_QueryShortcutX') 
          BEGIN 
              DROP TRIGGER DDLTrigger_QueryShortcutX ON DATABASE 
          END 
      
          EXEC sp_executeSQL @CreateTriggerSQL 
      
          IF EXISTS(SELECT TOP 1 1 FROM sys.synonyms WHERE name = 'QueryShortcutX') 
          BEGIN 
              DROP SYNONYM QueryShortcutX 
          END 
          CREATE SYNONYM QueryShortcutX FOR 
      
      1.RealColumnName    
      2.WhatEverText
      3.dbo.tests
      4.[No selection]
      5.dbo.tests.very.much
      
       DbName SchemaName      ObjectName      Alias           ObjectId
      1.TEST                  RealColumnName  RealColumnName  NULL --FN OBJECT_ID doesn't return value with only column name
      2.TEST                  WhatEverText    WhatEverText    NULL
      3.TEST  dbo             tests           dbo.tests       245575913
      4.Incorrect syntax near 'FOR'.
      5.TEST  very            much            very.much       NULL
      
      <TargetServerName>dbo</TargetServerName>
      <TargetDatabaseName>tests</TargetDatabaseName>
      <TargetSchemaName>very</TargetSchemaName>
      <TargetObjectName>much</TargetObjectName>