【问题标题】:Error creating view using stored procedure使用存储过程创建视图时出错
【发布时间】:2012-09-10 23:55:02
【问题描述】:

我有以下创建视图的存储过程:

ALTER PROC Proc_Guards_By_Client
(
    @client_number INT,
    @client_name   NVARCHAR(16)
)
AS
 BEGIN
   IF EXISTS(select * FROM sys.views where name = 'vwGuardsByClients')
   BEGIN
    EXEC ('CREATE VIEW vwGuardsByClients
    AS
    SELECT TOP 1000 
      cgt.[guard_id],
      sg.first_name,
      sg.last_name,
      sg.ammunition_quantity    
      FROM [sws4].[dbo].[client_guard_tracking] cgt
      INNER JOIN CLIENTS c
      ON c.client_number = cgt.client_number
      INNER JOIN security_guard sg
      ON sg.guard_id = cgt.guard_id
      WHERE cgt.client_number = @client_number
      OR c.client_name = @client_name
    ')
    END
    ELSE
    BEGIN
      EXEC ('UPDATE VIEW vwGuardsByClients
      SELECT TOP 1000 
      cgt.[guard_id],
      sg.first_name,
      sg.last_name,
      sg.ammunition_quantity    
      FROM [sws4].[dbo].[client_guard_tracking] cgt
      INNER JOIN CLIENTS c
      ON c.client_number = cgt.client_number
      INNER JOIN security_guard sg
      ON sg.guard_id = cgt.guard_id
      WHERE cgt.client_number = @client_number
      OR c.client_name = @client_name
    ')
    END

    IF @@ROWCOUNT = 0
        PRINT 'Warning: No rows were updated'
 END

但是当我执行它时,我得到:

Msg 156, Level 15, State 1, Line 2
Incorrect syntax near the keyword 'VIEW'.

Msg 137, Level 15, State 2, Line 14
Must declare the scalar variable "@client_number".

【问题讨论】:

  • 这里为什么使用动态SQL?
  • 这是使用存储过程创建/更新视图的要求
  • @abatishchev 你试过了吗?你不能在过程中调用CREATE VIEW
  • 这仍然是倒退。你的逻辑说:“如果视图存在,创建它!如果它不存在,编辑它!”另外我不知道你从哪里学到的语法“UPDATE VIEW”——它应该是“ALTER VIEW”。
  • “这是使用存储过程创建/更新视图的要求” - 这不是要求。某人决定的解决方案的一部分是“正确”的事情。如果您告诉我们要解决的原始问题是什么,而不是这个解决方案的问题,我们会做得更好。

标签: sql-server-2008 tsql stored-procedures dynamic-sql sql-view


【解决方案1】:

仍然存在各种问题。 as you did yesterday,当您承认它是倒退时,您的逻辑仍然倒退。怎么还是错了?现在它说:

如果视图已经存在:
      让我们来创造吧!

否则,如果视图尚不存在:
      我们来编辑吧!

下一个问题是您使用语法UPDATE VIEW。昨天您尝试使用CREATE OR REPLACE。两者都无效。你需要ALTER VIEW

您还在使用@@ROWCOUNT 来检查是否成功。这不是成功创建或更改视图的有效检查(它可能也不适合更新/删除检查,但这是另一个问题)。正如我昨天解释的那样,您应该为此使用TRY/CATCH

最后,您尝试在 EXEC() 内连接变量 - 您附加的字符串变量忽略了它包含撇号 (') 的可能性,这会破坏您的查询(此处可能存在 SQL 注入问题以及)。为此,您应该使用sp_executeSQL。事实上,最好不要浪费地重复所有视图代码:

ALTER PROCEDURE dbo.Proc_Guards_By_Client
  @client_number  INT,
  @client_name    NVARCHAR(16)
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @sql NVARCHAR(MAX) = N' VIEW dbo.vmGuardsByClient
    AS
      SELECT ... rest of view code ...
      WHERE cgt.client_number = ' + CONVERT(VARCHAR(12), @client_number) + 
       ' OR c.client_name = ''' + REPLACE(@client_name, '''', '''''') + ''';';

  SET @sql = CASE WHEN EXISTS
    (SELECT 1 FROM sys.views WHERE [object_id] = OBJECT_ID('dbo.vwGuardsByClients'))
    THEN N'ALTER' ELSE N'CREATE' + @sql;

  BEGIN TRY
    EXEC sp_executesql @sql;
  END TRY
  BEGIN CATCH
    PRINT ERROR_MESSAGE();
  END CATCH
END
GO

不过,我还是要问。为什么需要为不知道视图是否已经存在的特定视图创建存储过程?一旦您创建了这个视图一次,代码的CREATE VIEW 部分将如何再次执行?是不是你所有的用户都有dbo/sa权限,这个视图真的有随时被丢弃的危险吗?您是否尝试为每个客户创建一个视图?如果是这样,您最好考虑将客户端名称添加到视图名称中。在当前场景中,每次新客户端尝试运行您的代码时,您都将替换现有视图,然后当以前的用户从视图中选择时,他们会惊讶地发现他们不再看到自己的数据。

【讨论】:

    【解决方案2】:

    希望以下内容有所帮助。问题在于在构建语句时使用内联参数

    ALTER PROC Proc_Guards_By_Client
    (
      @client_number         INT
      ,@client_name              NVARCHAR(16)
    )
    AS
    BEGIN
    /****** Script for SelectTopNRows command from SSMS  ******/
    
      IF EXISTS(select * FROM sys.views where name = 'vwGuardsByClients')
      BEGIN
    
      EXEC ('
      UPDATE VIEW vwGuardsByClients
    
    SELECT TOP 1000 
      cgt.[guard_id],
      sg.first_name,
      sg.last_name,
      sg.ammunition_quantity    
      FROM [sws4].[dbo].[client_guard_tracking] cgt
      INNER JOIN CLIENTS c
      ON c.client_number = cgt.client_number
      INNER JOIN security_guard sg
      ON sg.guard_id = cgt.guard_id
      WHERE cgt.client_number = ' + cast(@client_number as varchar(10)) + 
      '  OR c.client_name = ''' + @client_name + '''
    ')
    -- Here you missing one character '
      END
    ELSE
    BEGIN
       EXEC ('CREATE VIEW vwGuardsByClients
       AS
       SELECT TOP 1000 
      cgt.[guard_id],
      sg.first_name,
      sg.last_name,
      sg.ammunition_quantity    
      FROM [sws4].[dbo].[client_guard_tracking] cgt
      INNER JOIN CLIENTS c
      ON c.client_number = cgt.client_number
      INNER JOIN security_guard sg
      ON sg.guard_id = cgt.guard_id
      WHERE cgt.client_number = ' + cast(@client_number as varchar(10)) + 
      ' OR c.client_name = ''' + @client_name + '''
    ')
    END
    
    --SELECT * from vwGuardsByClients
    
    IF @@ROWCOUNT = 0
    PRINT 'Warning: No rows were updated'
    

    结束

    【讨论】:

    • 这仍然是倒退。你的逻辑说:“如果视图存在,创建它!如果它不存在,编辑它!”由于各种原因,它也不会解析(不是你的错,但你只是延续了它们)。
    猜你喜欢
    • 2011-12-04
    • 1970-01-01
    • 2016-11-28
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多