我们可以将参数传递给SQL中的视图吗?


136

我们可以将参数传递给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 =?”
Epicurist

2
@Epicurist Parameters are out of the discussion过于大胆的声明。反例
Lukasz Szozda

Answers:


131

如前所述,您不能。

一种可能的解决方案是实现存储的功能,例如:

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

这使您可以通过以下方式将其用作普通视图:

SELECT * FROM v_emp(10)

此视图与视图之间的实际区别是什么?您可以分配用户权限以仅访问此功能吗?
MikeMurko 2012年

在MySQL中,您编写了一个存储过程,并使过程中的最后一条语句成为您要返回的结果集。
bobobobo

我们可以使用Java中的JDBC代码毫无问题地使用该请求吗?
mounaim 2014年

@MikeMurko一个重要的区别是,如果视图是视图,则可以查询有关视图列的架构/元数据。如果它存储了proc或函数,那么我想数据库可能无法为您提供该信息。
nagu

如果您拥有一组有权访问数据库的用户,并且不希望他们运行“从[view]中选择*”并影响性能,则可以授予对某些功能的访问权限,这将迫使他们提供过滤器参数例如,利用一组特定的索引。
Jmoney38 '18

34

不幸的是,有两种方法可以使用视图来实现。

您可以创建一个表值用户定义的函数,该函数采用所需的参数并返回查​​询结果

或者,您可以做几乎相同的事情,但是创建一个存储过程而不是用户定义的函数。

例如

存储过程看起来像

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阅读更多
萨斯顿

13

不,你不能像Mladen Prajdic所说。将视图视为表或表组合上的“静态过滤器”。例如:一个视图可能合并了表格OrderCustomer因此您将获得一个新的行“表格” Order以及包含客户名称和客户编号(表格的组合)的新列。或者,您可以创建一个仅从Order表中选择未处理订单的视图(静态过滤器)。

然后,您可以从视图中进行选择,就像从其他任何“正常”表中进行选择一样-所有“非静态”过滤都必须在视图外部进行(例如“为名为Miller的客户获取所有订单”或“获取未处理的订单”是在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
我认为这非常方便。与如何将参数传递到Web应用程序(例如,使用Java)类似。
4

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

尽管道格所说的是正确的,但现代数据库可以出色地完成“扩展”视图并有效地以与手动完成完整查询相同的结果来完成视图。因此,不要以为它会效率低下,因为数据库可能会让您感到惊讶-查看生成的查询计划。一个明显的例外是,如果视图具有影响输出的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 '16

6

没有。如果必须然后使用可以将参数传递到的用户定义函数。



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%')  


4

不,视图是静态的。您可以做的一件事(取决于SQl服务器的版本)是对视图建立索引。

在您的示例(仅查询一个表)中,索引视图仅查询带有索引的表没有任何好处,但是如果您对具有联接条件的表进行大量联接,则索引视图可以极大地提高性能。


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在许多方面都相似),但是要发现的方法不止一种:)试试看。
奥列格·梅尔尼科夫

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)

您的参数将保存并在Config表中更改。


2
如果您对回应的真实性有疑问,请在确认它至少是一个适当的解决方案之前不要发布它。就目前而言,这更多的是问题而不是答案。
CHB

此解决方案的一个问题是,如果查询在多个会话中运行,则可能会使用配置表中的错误数据
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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.