1

ビューを作成する次のストアド プロシージャがあります。

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".
4

2 に答える 2

5

まだまだいろいろな問題があります。昨日と同じように、論理が逆になっていることを認めたときは、まだ論理が逆になっています。それはまだどうして間違っているのですか?今それは言う:

ビューがすでに存在する場合:
      作成しましょう!

それ以外の場合、ビューがまだ存在しない場合:
      編集しましょう!

次の問題は、構文を使用することです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特権を持っていますか?このビューはいつでも削除される危険がありますか?クライアントごとにビューを作成しようとしていますか?その場合は、ビューの名前にクライアント名を追加することを検討してください。現在のシナリオでは、新しいクライアントがコードを実行しようとするたびに既存のビューを置き換えます。その後、前のユーザーがビューから選択すると、自分のデータが表示されなくなったことに驚かれることでしょう。

于 2012-09-18T18:07:25.077 に答える
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'

終わり

于 2012-09-18T17:19:16.973 に答える