我有以下创建视图的存储过程:
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".
答案 0 :(得分:5)
仍有各种问题。当你承认它是倒退的时候,你的逻辑仍然向前,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权限吗?此视图是否真的有被丢弃的危险?您是否尝试为每个客户创建一个视图?如果是这样,您最好考虑将客户端名称添加到视图的名称中。在当前场景中,每当新客户端尝试运行代码时,您将替换现有视图,然后当先前用户从视图中进行选择时,他们会惊讶于他们不再看到自己的数据。 / p>
答案 1 :(得分: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'
END