【问题标题】:Can we pass parameters to a view in SQL?我们可以将参数传递给 SQL 中的视图吗?
【发布时间】:2010-12-13 19:45:31
【问题描述】:

我们可以将参数传递给 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 Parameters are out of the discussion 过于大胆的声明。 Counterexample

标签: sql sql-server parameters parameter-passing views


【解决方案1】:

虽然问题得到了很好的回答,但我想补充一点。大多数时候,我们将视图视为发送数据的查询,但视图不止于此……视图也可用于更新基础表中的数据。您可以右键单击 SSMS 中的视图,您将找到“编辑前 200 行”选项。

我相信要启用这种编辑数据的能力,因为对于如何为 View 编写查询有一定的限制,它需要是一个静态查询。

因此,与发送查询数据并关闭的用户定义函数或存储过程不同,视图可以维持实时连接(例如在 Microsoft Access 链接表/视图中)并将更新写回数据库。

因此,如果您只想获取具有某些动态标准的数据集,则应使用带有所需参数的 UDF/SP。

【讨论】:

    【解决方案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
    )
    

    【讨论】:

    • 请记住,您不能在SELECT 中轻松使用 SP 选项:read more
    【解决方案3】:

    只需将此视图用于带有所需参数(例如在 SQL Server 中)和查询视图中的参数值的存储过程。

    使用 View/ 表创建存储过程:_spCallViewWithParameters

    执行程序:

    【讨论】:

      【解决方案4】:

      我根据自己的需要实现了这个任务,如下所示

      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
      

      【讨论】:

        【解决方案5】:

        通常视图没有参数化。但是你总是可以注入一些参数。例如使用session context:

        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 Demo

        同样适用于Oracle(当然上下文函数的语法不同)。

        【讨论】:

        • 我认为这很方便。类似于如何将参数传递给 Web 应用程序,例如在 Java 中。
        • 简单实用!换句话说……完美!谢谢!
        • 我累了。添加 WHERE COUL = SESSION_CONTEXT(N'Ket');在视图中导致错误“SESSION_CONTEXT”不是可识别的内置函数名称。
        • @user123456 您必须使用 SQL Server 2016 及更高版本或 Azure SQL 数据库
        【解决方案6】:

        我有一个想法,我还没有尝试过。你可以这样做:

        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)
        

        您的参数将在配置表中保存和更改。

        【讨论】:

        • 如果您对回复的真实性有疑问,请在验证它至少是一个足够解决方案之前不要发布它。就目前而言,这更像是一个问题而不是一个答案。
        • 此解决方案的一个问题是,如果查询在多个会话中运行,则可能会使用配置表中的错误数据
        【解决方案7】:

        您的视图可以引用一些包含您的参数的外部表。

        正如其他人提到的,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
        

        【讨论】:

        • 应该可以(PL/SQL 和 T-SQL 在很多方面都很相似),但是找到的方法不止一种:)试一试。
        【解决方案8】:

        为什么在视图中需要一个参数?您可能只使用WHERE 子句。

        create view v_emp as select * from emp ;
        

        您的查询应该可以完成这项工作:

        select * from v_emp where emp_id=&eno;
        

        【讨论】:

        • 在某些情况下,当它是表的WHERE 而不是视图的WHERE 时,会有很大的性能提升。
        • 虽然 Doug 所说的有些正确,但现代数据库可以出色地完成巧妙地“扩展”视图并有效地以与手动执行完整查询相同的结果结束。所以不要假设它会因为数据库可能会让你吃惊而低效——看看生成的查询计划。一个值得注意的例外是视图有一个影响输出的 GROUP BY 子句 - 在这种情况下,您无法从“外部”执行 WHERE。
        【解决方案9】:

        这是我目前还没有看到的一个选项:

        只需将要限制的列添加到视图中:

        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)
        

        【讨论】:

          【解决方案10】:

          您可以绕过只是为了运行视图,SQL 会大喊大叫,但只需执行此操作并运行它!无法保存。

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

          【讨论】:

            【解决方案11】:

            如果你不想使用某个函数,你可以使用类似这样的东西

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

            希望对你有帮助

            【讨论】:

              【解决方案12】:

              据我所知,视图可以类似于选择命令。 您还可以向此选择添加参数,例如在这样的 where 语句中:

               WHERE  (exam_id = @var)
              

              【讨论】:

                【解决方案13】:

                在不使用存储过程或函数的情况下,一种巧妙的方法是在数据库中创建一个设置表,其中包含 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
                

                【讨论】:

                • 用它来请求查看会很糟糕。但是作为配置/阶段/环境,使用这样的隐藏参数真的很有用。对我来说是一个加号。
                【解决方案14】:

                我们可以编写一个带有输入参数的存储过程,然后使用该存储过程从视图中获取结果集。 请参阅下面的示例。

                存储过程是

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

                【讨论】:

                  【解决方案15】:

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

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

                  【讨论】:

                    【解决方案16】:

                    视图只不过是一个预定义的“SELECT”语句。所以唯一真正的答案是:不,你不能。

                    我认为你真正想做的是创建一个存储过程,原则上你可以使用任何有效的 SQL 来做任何你想做的事情,包括接受参数和选择数据。

                    您似乎真的只需要在从视图中选择时添加一个 where 子句,但您并没有真正提供足够的细节来确定。

                    【讨论】:

                      【解决方案17】:

                      如前所述,您不能。

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

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

                      这允许您将其用作普通视图,其中:

                      SELECT * FROM v_emp(10)
                      

                      【讨论】:

                      • 这和视图之间有什么实际区别?您可以分配用户权限以仅访问此功能吗?
                      • 在 MySQL 中,您编写一个存储过程,并将过程中的最后一条语句作为您想要返回的结果集。
                      • 我们可以从 java 中的 JDBC 代码中毫无问题地使用该请求吗?
                      • @MikeMurko 一个重要的区别是,如果是视图,则可以查询有关视图列的模式/元数据。如果它的存储过程或函数,那么我猜数据库可能无法为您提供该信息。
                      • 如果您有一组可以访问您的数据库的用户,并且您不希望他们运行“select * from [view]”并影响性能,您可以授予对某些功能的访问权限,这将迫使他们提供过滤器参数,例如,利用一组索引。
                      【解决方案18】:

                      没有 您可以将参数传递给视图中的过程

                      【讨论】:

                        【解决方案19】:

                        不,查询视图与从表中选择查询没有什么不同。

                        要做你想做的事,使用带有一个或多个参数的table-valued user-defined function

                        【讨论】:

                          【解决方案20】:

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

                          然后,您可以像从任何其他“正常”表中选择一样从视图中进行选择 - 所有“非静态”过滤都必须在视图之外完成(例如“获取名为 Miller 的客户的所有订单”或“获取 12 月 24 日收到的未处理订单”)。

                          【讨论】:

                            【解决方案21】:

                            没有。 如果您必须使用用户定义的函数,您可以将参数传递给该函数。

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2015-10-26
                              • 2023-04-01
                              • 2015-09-23
                              • 1970-01-01
                              • 1970-01-01
                              • 2013-06-11
                              相关资源
                              最近更新 更多