SQLでビューにパラメーターを渡すことはできますか?


138

Microsoft SQL Serverのビューにパラメーターを渡すことはできますか?

create view次の方法で試しましたが、うまくいきません。

create or replace view v_emp(eno number) as select * from emp where emp_id=&eno;

ビューは、選択クエリの保存されたSQLテキストです。パラメータは議論外です。格納されたクエリがフィルターに使用する列を返す場合、呼び出し元のクエリで実行できます。例:「SELECT * FROM v_emp WHERE emp_id =?」
エピキュリスト2017年

2
@エピキュリストがParameters are out of the discussion大胆すぎる。反例
Lukasz Szozda

回答:


132

すでに述べたようにできません。

可能な解決策は、次のようなストアド関数を実装することです。

CREATE FUNCTION v_emp (@pintEno INT)
RETURNS TABLE
AS
RETURN
   SELECT * FROM emp WHERE emp_id=@pintEno;

これにより、次のように通常のビューとして使用できます。

SELECT * FROM v_emp(10)

これとビューの実際的な違いは何ですか?この機能にのみアクセスするユーザー権限を割り当てることができますか?
MikeMurko

MySQLでは、ストアドプロシージャを記述し、プロシージャの最後のステートメントを返して欲しい結果セットにします。
bobobobo 2013

JavaのJDBCコードから問題なくその要求を使用できますか?
mounaim 2014

@MikeMurkoの1つの重要な違いは、ビューの列に関するスキーマ/メタデータは、ビューの場合はクエリできることです。ストアドプロシージャまたは関数の場合、データベースがその情報を提供できない可能性があります。
nagu

データベースにアクセスできる一連のユーザーがいて、「select * from [view]」を実行してパフォーマンスに影響を与えたくない場合は、特定の機能へのアクセスを許可して、強制的にフィルターパラメーターを提供することができます。たとえば、特定のインデックスのセットを活用します。
Jmoney38

35

残念ながら、ビューを使用してどちらも実行できない2つの方法があります。

必要なパラメーターを取り、クエリ結果を返すテーブル値のユーザー定義関数を作成できます。

または、ほとんど同じことを実行できますが、ユーザー定義関数の代わりにストアドプロシージャを作成します。

例えば

ストアドプロシージャは次のようになります

CREATE PROCEDURE s_emp
(
    @enoNumber INT
) 
AS 
SELECT
    * 
FROM
    emp 
WHERE 
    emp_id=@enoNumber

または、ユーザー定義関数は次のようになります

CREATE FUNCTION u_emp
(   
    @enoNumber INT
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT    
        * 
    FROM    
        emp 
    WHERE     
        emp_id=@enoNumber
)

ちょうどあなたがでSPのオプションを使用できないことに注意してSELECT簡単:続きを読みます
saastn

13

Mladen Prajdicが言ったように、あなたはできません。ビューは、テーブルまたはテーブルの組み合わせの「静的フィルター」と考えてください。たとえば、ビューはテーブルOrderを組み合わせるCustomer場合があるためOrder、顧客の名前と顧客番号(テーブルの組み合わせ)を含む新しい列とともに、行の新しい「テーブル」を取得します。または、Orderテーブルから未処理の注文のみを選択するビューを作成することもできます(静的フィルター)。

次に、他の「通常の」テーブルから選択するのと同じように、ビューから選択します。すべての「非静的」フィルタリングは、ビューの外で行う必要があります(「ミラーと呼ばれる顧客のすべての注文を取得する」または「未処理の注文を取得する」など) 12月24日に登場しました」)。


12

通常、ビューはパラメーター化されません。しかし、常にいくつかのパラメーターを注入することができます。たとえば、セッションコンテキストを使用します

CREATE VIEW my_view
AS
SELECT *
FROM tab
WHERE num = SESSION_CONTEXT(N'my_num');

呼び出し:

EXEC sp_set_session_context 'my_num', 1; 
SELECT * FROM my_view;

そしてもう一つ:

EXEC sp_set_session_context 'my_num', 2; 
SELECT * FROM my_view;

DBFiddleデモ

Oracleにも同じことが当てはまります(もちろん、コンテキスト関数の構文は異なります)。


2
これはかなり便利だと思います。パラメータをJavaなどのWebアプリに渡す方法と同様です。
18年

1
簡単で機能的!言い換えれば...完璧!ありがとうございました!
Riccardo Bassilichi

疲れた。WHERE COUL = SESSION_CONTEXT(N'Ket ');を追加します。ビューの結果のエラー 'SESSION_CONTEXT'は、認識されている組み込み関数名ではありません。
user123456

@ user123456 SQL Server 2016以降またはAzure SQLデータベースを使用する必要があります
Lukasz Szozda

9

ビューにパラメータが必要なのはなぜですか?WHERE句を使用するだけかもしれません。

create view v_emp as select * from emp ;

そしてあなたのクエリは仕事をするべきです:

select * from v_emp where emp_id=&eno;

11
ビューWHEREではなくテーブルの場合、パフォーマンスが大幅に向上する場合がありWHEREます。
Doug_Ivison 14

Dougの言うことはいくぶん真実ですが、最近のデータベースは、ビューをスマートに「拡張」し、完全なクエリを手動で実行した場合と同じ結果になるという驚くべき仕事を行うことができます。データベースがあなたを驚かせるかもしれないので、それが非効率的であると仮定しないでください-生成されたクエリプランを見てください。注目すべき例外は、ビューに出力に影響を与えるGROUP BY句がある場合です-この場合、「外部」からWHEREを実行できませんでした。
Simon_Weaver

8

ストアドプロシージャや関数を使用せずにそれを行うための手っ取り早い方法は、列Id、Param1、Param2などを使用してデータベースに設定テーブルを作成することです。Id= 1、Param1 = 0、Param2の値を含む行をそのテーブルに挿入します。 = 0など。その後、ビューのそのテーブルに結合を追加して目的の効果を作成し、ビューを実行する前に設定テーブルを更新できます。複数のユーザーが設定テーブルを更新してビューを同時に実行している場合、問題が発生する可能性がありますが、それ以外の場合は問題なく機能します。何かのようなもの:

CREATE VIEW v_emp 
AS 
SELECT      * 
FROM        emp E
INNER JOIN  settings S
ON          S.Id = 1 AND E.emp_id = S.Param1

それを表示する要求に使用するのはひどいでしょう。しかし、そのような隠されたパラメーターを使用することは、構成/ステージ/環境として実際に使用できます。そのためのプラス。
TPAKTOPA



5

ビューは、定義済みの「SELECT」ステートメントにすぎません。したがって、唯一の本当の答えは次のとおりです。いいえ、できません。

私が本当にやりたいことは、ストアドプロシージャを作成することだと思います。ここでは、原則として、有効なSQLを使用して、パラメーターの受け入れやデータの選択など、好きなことを何でも実行できます。

ただし、ビューから選択するときにwhere句を追加する必要があるだけのようですが、確かに十分な詳細を提供していません。


5

入力パラメータを使用してストアドプロシージャを記述し、そのストアドプロシージャを使用してビューから結果セットを取得できます。以下の例を参照してください。

ストアドプロシージャは

CREATE PROCEDURE [dbo].[sp_Report_LoginSuccess] -- [sp_Report_LoginSuccess] '01/01/2010','01/30/2010'
@fromDate datetime,
@toDate datetime,
@RoleName varchar(50),
@Success int
as
If @RoleName != 'All'
Begin
   If @Success!=2
   Begin
   --fetch based on true or false
  Select * from vw_Report_LoginSuccess
  where logindatetime between  dbo.DateFloor(@fromDate) and dbo.DateSieling(@toDate)
  And RTrim(Upper(RoleName)) = RTrim(Upper(@RoleName)) and Success=@Success
   End
   Else
   Begin
    -- fetch all
  Select * from vw_Report_LoginSuccess
  where logindatetime between  dbo.DateFloor(@fromDate) and dbo.DateSieling(@toDate)
  And RTrim(Upper(RoleName)) = RTrim(Upper(@RoleName))
   End

End
Else
Begin
   If @Success!=2
   Begin
  Select * from vw_Report_LoginSuccess
  where logindatetime between  dbo.DateFloor(@fromDate) and dbo.DateSieling(@toDate)
  and Success=@Success
 End
 Else
 Begin
  Select * from vw_Report_LoginSuccess
  where logindatetime between  dbo.DateFloor(@fromDate) and dbo.DateSieling(@toDate)
 End

End

結果セットを取得できるビューは

CREATE VIEW [dbo].[vw_Report_LoginSuccess]
AS
SELECT     '3' AS UserDetailID, dbo.tblLoginStatusDetail.Success, CONVERT(varchar, dbo.tblLoginStatusDetail.LoginDateTime, 101) AS LoginDateTime,
                      CONVERT(varchar, dbo.tblLoginStatusDetail.LogoutDateTime, 101) AS LogoutDateTime, dbo.tblLoginStatusDetail.TokenID,
                      dbo.tblUserDetail.SubscriberID, dbo.aspnet_Roles.RoleId, dbo.aspnet_Roles.RoleName
FROM         dbo.tblLoginStatusDetail INNER JOIN
                      dbo.tblUserDetail ON dbo.tblLoginStatusDetail.UserDetailID = dbo.tblUserDetail.UserDetailID INNER JOIN
                      dbo.aspnet_UsersInRoles ON dbo.tblUserDetail.UserID = dbo.aspnet_UsersInRoles.UserId INNER JOIN
                      dbo.aspnet_Roles ON dbo.aspnet_UsersInRoles.RoleId = dbo.aspnet_Roles.RoleId
WHERE     (dbo.tblLoginStatusDetail.Success = 0)
UNION all
SELECT     dbo.tblLoginStatusDetail.UserDetailID, dbo.tblLoginStatusDetail.Success, CONVERT(varchar, dbo.tblLoginStatusDetail.LoginDateTime, 101)
                      AS LoginDateTime, CONVERT(varchar, dbo.tblLoginStatusDetail.LogoutDateTime, 101) AS LogoutDateTime, dbo.tblLoginStatusDetail.TokenID,
                      dbo.tblUserDetail.SubscriberID, dbo.aspnet_Roles.RoleId, dbo.aspnet_Roles.RoleName
FROM         dbo.tblLoginStatusDetail INNER JOIN
                      dbo.tblUserDetail ON dbo.tblLoginStatusDetail.UserDetailID = dbo.tblUserDetail.UserDetailID INNER JOIN
                      dbo.aspnet_UsersInRoles ON dbo.tblUserDetail.UserID = dbo.aspnet_UsersInRoles.UserId INNER JOIN
                      dbo.aspnet_Roles ON dbo.aspnet_UsersInRoles.RoleId = dbo.aspnet_Roles.RoleId
WHERE     (dbo.tblLoginStatusDetail.Success = 1) AND (dbo.tblUserDetail.SubscriberID LIKE N'P%')  

5

私が知っているように、ビューはselectコマンドのようなものにすることができます。このselectにパラメーターを追加することもできます。たとえば、whereステートメントは次のようになります。

 WHERE  (exam_id = @var)

4

いいえ、ビューは静的です。(SQlサーバーのバージョンに応じて)できることの1つは、ビューのインデックスを作成することです。

例(1つのテーブルのみをクエリする)では、インデックス付きビューは、インデックスを含むテーブルをクエリするだけではメリットがありませんが、結合条件を持つテーブルで多くの結合を実行している場合、インデックス付きビューはパフォーマンスを大幅に向上させることができます。


4

関数を使用したくない場合は、次のようなものを使用できます

-- VIEW
CREATE VIEW [dbo].[vwPharmacyProducts]
AS
SELECT     PharmacyId, ProductId
FROM         dbo.Stock
WHERE     (TotalQty > 0)

-- Use of view inside a stored procedure
CREATE PROCEDURE [dbo].[usp_GetProductByFilter]
(   @pPharmacyId int ) AS

IF @pPharmacyId = 0 BEGIN SET @pPharmacyId = NULL END

SELECT  P.[ProductId], P.[strDisplayAs] FROM [Product] P
WHERE (P.[bDeleted] = 0)
    AND (P.[ProductId] IN (Select vPP.ProductId From vwPharmacyProducts vPP
                           Where vPP.PharmacyId = @pPharmacyId)
                       OR @pPharmacyId IS NULL
        )

それが役に立てば幸い



2

ここに私が今まで見たことがないオプションがあります:

制限したい列をビューに追加するだけです:

create view emp_v as (
select emp_name, emp_id from emp;
)

select emp_v.emp_name from emp_v
where emp_v.emp_id = (id to restrict by)

1

ビューを実行するためだけにバイパスすることができます。SQLは泣き叫びますが、これを実行するだけです。保存できません。

create or replace view v_emp(eno number) as select * from emp where (emp_id = @Parameter1);

1

ビューは、パラメーターを含む外部テーブルを参照できます。

他の人が述べたように、SQL Serverのビューは外部入力パラメーターを持つことができません。ただし、CTEを使用すると、ビュー内の変数を簡単に偽造できます。SQL Serverのバージョンでテスト実行できます。

CREATE VIEW vwImportant_Users AS
WITH params AS (
    SELECT 
    varType='%Admin%', 
    varMinStatus=1)
SELECT status, name 
    FROM sys.sysusers, params
    WHERE status > varMinStatus OR name LIKE varType

SELECT * FROM vwImportant_Users

出力を生成する:

status  name
12      dbo
0       db_accessadmin
0       db_securityadmin
0       db_ddladmin

また経由で JOIN

WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name 
    FROM sys.sysusers INNER JOIN params ON 1=1
    WHERE status > varMinStatus OR name LIKE varType

また経由で CROSS APPLY

WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name 
    FROM sys.sysusers CROSS APPLY params
    WHERE status > varMinStatus OR name LIKE varType

1
(PL / SQLとT-SQLは多くの点で似ています)はずですが、調べる方法は複数あります:)試してみてください。
Oleg Melnikov

0

私はまだ試していません。できるよ:

CREATE VIEW updated_customers AS
SELECT * FROM customer as aa
LEFT JOIN customer_rec as bb
ON aa.id = bb.customer_id
WHERE aa.updated_at between (SELECT start_date FROM config WHERE active = 1) 
and (SELECT end_date FROM config WHERE active = 1)

パラメータが保存され、構成テーブルに変更されます。


2
回答の信憑性について疑問がある場合は、少なくとも適切な解決策であることを確認する前に投稿しないでください。現状では、これは答えというよりは質問の問題です。
1

このソリューションの1つの問題は、クエリが複数のセッションで実行されている場合、構成テーブルの誤ったデータが使用される可能性があることです
User1010

0

私は自分のニーズのために次のようにこのタスクを実現しました

set nocount on;

  declare @ToDate date = dateadd(month,datediff(month,0,getdate())-1,0)

declare @year varchar(4)  = year(@ToDate)
declare @month varchar(2) = month(@ToDate)

declare @sql nvarchar(max)
set @sql = N'
    create or alter view dbo.wTempLogs
    as
    select * from dbo.y2019
    where
        year(LogDate) = ''_year_''
        and 
        month(LogDate) = ''_month_''    '

select @sql = replace(replace(@sql,'_year_',@year),'_month_',@month)

execute sp_executesql @sql

declare @errmsg nvarchar(max)
    set @errMsg = @sql
    raiserror (@errMsg, 0,1) with nowait
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.