¿Podemos pasar parámetros a una vista en SQL?

138

¿Podemos pasar un parámetro a una vista en Microsoft SQL Server?

Traté de create viewla siguiente manera, pero no funciona:

create or replace view v_emp(eno number) as select * from emp where emp_id=&eno;
arunachalam
fuente
Una vista es un texto sql almacenado de una consulta de selección. Los parámetros están fuera de discusión. Cuando su consulta almacenada devuelve la columna donde desea filtrar, puede hacerlo en la consulta de llamada. Por ejemplo, "SELECT * FROM v_emp WHERE emp_id =?"
Epicurista
2
@Epicurist Declaración Parameters are out of the discussiondemasiado audaz. Contraejemplo
Lukasz Szozda

Respuestas:

132

Como ya se dijo, no puedes.

Una posible solución sería implementar una función almacenada, como:

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

Esto le permite usarlo como una vista normal, con:

SELECT * FROM v_emp(10)
Alex Bagnolini
fuente
¿Cuáles son las diferencias prácticas entre esto y una vista? ¿Se pueden asignar permisos de usuario para acceder solo a esta función?
MikeMurko
En MySQL, escribe un procedimiento almacenado y hace que la última instrucción del procedimiento sea el conjunto de resultados que desea devolver.
bobobobo
¿Podemos usar esa solicitud sin ningún problema del código JDBC en Java?
mounaim
@MikeMurko una diferencia importante es que el esquema / metadatos sobre las columnas de una vista se pueden consultar si es una vista. Si es un proceso almacenado o una función, entonces supongo que las bases de datos pueden no ser capaces de darle esa información.
nagu
Si tiene un conjunto de usuarios que tienen acceso a su base de datos y no desea que ejecuten "select * from [view]" y afecten el rendimiento, puede otorgarles acceso a ciertas funciones, lo que los obligaría a proporcionar parámetros de filtro que, por ejemplo, aprovechan un determinado conjunto de índices.
Jmoney38
35

Hay 2 formas de lograr lo que desea desafortunadamente, ninguna de las cuales se puede hacer con una vista.

Puede crear una función definida por el usuario con valores de tabla que tome el parámetro que desee y devuelva un resultado de consulta

O puede hacer casi lo mismo pero crear un procedimiento almacenado en lugar de una función definida por el usuario.

Por ejemplo

el procedimiento almacenado se vería así

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

O la función definida por el usuario se vería así

CREATE FUNCTION u_emp
(   
    @enoNumber INT
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT    
        * 
    FROM    
        emp 
    WHERE     
        emp_id=@enoNumber
)
Gavin
fuente
Solo tenga en cuenta que no puede usar la opción SP SELECTfácilmente: lea más .
Saastn
13

No, no puedes, como dijo Mladen Prajdic. Piense en una vista como un "filtro estático" en una tabla o una combinación de tablas. Por ejemplo: una vista puede combinar tablas Ordery, Customerpor lo tanto, obtiene una nueva "tabla" de filas Orderjunto con nuevas columnas que contienen el nombre y el número del cliente (combinación de tablas). O puede crear una vista que seleccione solo pedidos sin procesar de la Ordertabla (filtro estático).

Luego seleccionaría de la vista como seleccionaría de cualquier otra tabla "normal": todo el filtrado "no estático" debe realizarse fuera de la vista (como "Obtener todos los pedidos para clientes llamados Miller" u "Obtener pedidos no procesados" que entró el 24 de diciembre ").

Thorsten Dittmar
fuente
12

Normalmente las vistas no están parametrizadas. Pero siempre puedes inyectar algunos parámetros. Por ejemplo, usando el contexto de sesión :

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

Invocación:

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

Y otro:

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

DBFiddle Demo

Lo mismo es aplicable para Oracle (por supuesto, la sintaxis para la función de contexto es diferente).

Lukasz Szozda
fuente
2
Creo que esto es bastante útil. Similar a cómo se pueden pasar los parámetros a las aplicaciones web, por ejemplo, en Java.
8 de
1
¡fácil y funcional! En otras palabras ... ¡perfecto! ¡Gracias!
Riccardo Bassilichi
Estoy cansado. Agregar WHERE COUL = SESSION_CONTEXT (N'Ket '); a la vista, el error 'SESSION_CONTEXT' no es un nombre de función incorporado reconocido.
user123456
@ user123456 Tiene que usar SQL Server 2016 y superior o Azure SQL Database
Lukasz Szozda
9

¿Por qué necesita un parámetro a la vista? Podrías usar la WHEREcláusula.

create view v_emp as select * from emp ;

y su consulta debe hacer el trabajo:

select * from v_emp where emp_id=&eno;
Mahesh
fuente
11
En algunos casos habrá una gran mejora en el rendimiento, cuando sea WHEREpara la mesa, en lugar de WHEREpara la vista.
Doug_Ivison
Si bien lo que Doug dice es algo cierto, las bases de datos modernas pueden hacer un trabajo notable al 'expandir' de manera inteligente una vista y terminar efectivamente con el mismo resultado que si solo hiciera la consulta completa manualmente. Por lo tanto, no asuma que será ineficiente porque la base de datos puede sorprenderlo: mire el plan de consulta generado. Una excepción notable sería si la vista tiene una cláusula GROUP BY que afecta a la salida, en cuyo caso no podría hacer el WHERE desde el 'exterior'.
Simon_Weaver
8

Una forma hacky de hacerlo sin procedimientos almacenados o funciones sería crear una tabla de configuración en su base de datos, con las columnas Id, Param1, Param2, etc. Inserte una fila en esa tabla que contenga los valores Id = 1, Param1 = 0, Param2 = 0, etc. Luego, puede agregar una unión a esa tabla en su vista para crear el efecto deseado y actualizar la tabla de configuración antes de ejecutar la vista. Si tiene varios usuarios actualizando la tabla de configuración y ejecutando la vista al mismo tiempo, las cosas podrían salir mal, pero de lo contrario debería funcionar bien. Algo como:

CREATE VIEW v_emp 
AS 
SELECT      * 
FROM        emp E
INNER JOIN  settings S
ON          S.Id = 1 AND E.emp_id = S.Param1
Bozonik
fuente
sería terrible usarlo para una solicitud de visualización. Pero es realmente utilizable, como configuración / etapa / entorno, para usar estos parámetros ocultos. Un plus para mí por eso.
TPAKTOPA
6

No. si debe usar una función definida por el usuario a la que puede pasar parámetros.

Mladen Prajdic
fuente
5

Una vista no es más que una declaración predefinida 'SELECCIONAR'. Entonces la única respuesta real sería: No, no puedes.

Creo que lo que realmente quiere hacer es crear un procedimiento almacenado, donde, en principio, puede usar cualquier SQL válido para hacer lo que quiera, incluidos los parámetros de aceptación y los datos seleccionados.

Sin embargo, parece probable que realmente solo necesite agregar una cláusula where cuando seleccione desde su vista, pero realmente no proporcionó suficientes detalles para estar seguro.

Kris
fuente
5

podemos escribir un procedimiento almacenado con parámetros de entrada y luego usar ese procedimiento almacenado para obtener un conjunto de resultados de la vista. ver ejemplo a continuación.

el procedimiento almacenado es

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

y la vista desde la que podemos obtener el conjunto de resultados es

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%')  
sunil
fuente
5

Como sé, la vista puede ser algo así como el comando de selección. También puede agregar parámetros a esta selección, por ejemplo, en declaraciones como esta:

 WHERE  (exam_id = @var)
Reza Ameri
fuente
4

No, una vista es estática. Una cosa que puede hacer (dependiendo de la versión del servidor SQl) es indexar una vista.

En su ejemplo (consultando solo una tabla), una vista indexada no tiene ningún beneficio simplemente al consultar la tabla con un índice, pero si está haciendo muchas uniones en tablas con condiciones de unión, una vista indexada puede mejorar enormemente el rendimiento.

Juan
fuente
4

Si no quieres usar una función, puedes usar algo como esto

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

Espero que ayude

Adnan Badar
fuente
3

no, puede pasar el parámetro al procedimiento en vista

aicuxiao
fuente
2

Aquí hay una opción que no he visto hasta ahora:

Simplemente agregue la columna que desea restringir a la vista:

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)
FarajDaoud
fuente
1

Puede omitir solo para ejecutar la vista, SQL se pondrá a llorar pero solo haga esto y ejecútelo. No puedes salvar.

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

Su vista puede hacer referencia a alguna tabla externa que contiene sus parámetros.

Como otros mencionaron, la vista en SQL Server no puede tener parámetros de entrada externos. Sin embargo, puede falsificar fácilmente una variable en su vista usando CTE. Puede ejecutarlo en su versión de 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

rendimiento de salida:

status  name
12      dbo
0       db_accessadmin
0       db_securityadmin
0       db_ddladmin

también a través de 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

también a través de 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
Oleg Melnikov
fuente
1
Debería (PL / SQL y T-SQL son similares en muchos aspectos), pero hay más de una forma de averiguarlo :) Pruébalo.
Oleg Melnikov
0

Tengo una idea que aún no he probado. Tu puedes hacer:

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)

Sus parámetros se guardarán y cambiarán en la tabla de configuración.

Emman
fuente
2
Si tiene dudas sobre la veracidad de una respuesta, no la publique antes de verificar que es al menos una solución adecuada . Tal como está, esto es más una pregunta que una respuesta.
chb
Un problema con esta solución será que si la consulta se ejecuta en varias sesiones, se podrían usar los datos incorrectos en la tabla de configuración
User1010
0

Realicé esta tarea para mis necesidades de la siguiente manera

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
cretalex
fuente