Czy możemy przekazać parametry do widoku w SQL?


140

Czy możemy przekazać parametr do widoku w Microsoft SQL Server?

Próbowałem create vieww następujący sposób, ale to nie działa:

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

Widok to przechowywany tekst sql zapytania wybierającego. Parametry nie są przedmiotem dyskusji. Gdy zapisane zapytanie zwraca kolumnę, w której chcesz filtrować, możesz to zrobić w zapytaniu wywołującym. Np. „SELECT * FROM v_emp WHERE emp_id =?”
Epicurist

2
@Epicurist Parameters are out of the discussionZbyt odważne stwierdzenie. Counterexample
Lukasz Szozda

Odpowiedzi:


134

Jak już wspomniano, nie możesz.

Możliwym rozwiązaniem byłoby zaimplementowanie przechowywanej funkcji, takiej jak:

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

Pozwala to używać go jako normalnego widoku, z:

SELECT * FROM v_emp(10)

Jakie są praktyczne różnice między tym a poglądem? Czy możesz przypisać użytkownikom uprawnienia dostępu tylko do tej funkcji?
MikeMurko

W MySQL piszesz procedurę składowaną i ostatnią instrukcją w procedurze jest zestaw wyników, który chcesz zwrócić.
bobobobo

czy możemy bez problemu użyć tego żądania z kodu JDBC w javie?
mounaim

@MikeMurko Jedną ważną różnicą jest to, że schemat / metadane dotyczące kolumn widoku można odpytać, jeśli jest to widok. Jeśli jest to przechowywany proces lub funkcja, to wydaje mi się, że bazy danych mogą nie być w stanie podać tych informacji.
nagu

Jeśli masz grupę użytkowników, którzy mają dostęp do Twojej bazy danych i nie chcesz, aby działali „wybierz * z [widok]” i wpłynęli na wydajność, możesz przyznać dostęp do niektórych funkcji, co zmusiłoby ich do podania parametrów filtru które, na przykład, wykorzystują pewien zestaw indeksów.
Jmoney38

35

Niestety, istnieją 2 sposoby osiągnięcia tego, co chcesz, ale nie można tego zrobić za pomocą widoku.

Możesz utworzyć funkcję zdefiniowaną przez użytkownika z wartościami tabeli, która przyjmuje żądany parametr i zwraca wynik zapytania

Lub możesz zrobić prawie to samo, ale utworzyć procedurę składowaną zamiast funkcji zdefiniowanej przez użytkownika.

Na przykład

jak wyglądałaby procedura składowana

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

Albo jak wyglądałaby funkcja zdefiniowana przez użytkownika

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

Pamiętaj tylko, że nie możesz łatwo użyć opcji SP SELECT: przeczytaj więcej .
saastn

13

Nie, nie możesz, jak powiedział Mladen Prajdic. Pomyśl o widoku jako o „filtrze statycznym” tabeli lub kombinacji tabel. Na przykład: widok może łączyć tabele Orderi Customertak pojawi się nowy „stół” z wierszy Orderwraz z nowymi kolumnami zawierającymi nazwę klienta i numer klienta (połączenie tabel). Możesz też utworzyć widok, który wybiera tylko nieprzetworzone zamówienia z Ordertabeli (filtr statyczny).

Następnie wybierzesz z widoku, tak jak w przypadku każdej innej „normalnej” tabeli - wszystkie „niestatyczne” filtrowanie należy wykonać poza widokiem (np. „Pobierz wszystkie zamówienia dla klientów o nazwie Miller” lub „Otrzymuj nieprzetworzone zamówienia które nadeszło 24 grudnia ”).


12

Zwykle widoki nie są sparametryzowane. Ale zawsze możesz wprowadzić jakieś parametry. Na przykład używając kontekstu sesji :

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

Wezwanie:

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

I kolejny:

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

DBFiddle Demo

To samo dotyczy Oracle (oczywiście składnia funkcji kontekstu jest inna).


2
Myślę, że to jest bardzo przydatne. Podobnie jak w przypadku przekazywania parametrów do aplikacji internetowych, np. W Javie.
czterdzieści

1
łatwe i funkcjonalne! Innymi słowy ... idealnie! Dziękuję Ci!
Riccardo Bassilichi

Próbowałem. Dodanie WHERE COUL = SESSION_CONTEXT (N'Ket '); w związku z tym wynik błędu „SESSION_CONTEXT” nie jest rozpoznawaną nazwą funkcji wbudowanej.
user123456

@ user123456 Musisz używać SQL Server 2016 i nowszych wersji lub Azure SQL Database
Łukasz Szozda

9

Dlaczego potrzebujesz widoku parametru? Możesz po prostu użyć WHEREklauzuli.

create view v_emp as select * from emp ;

a twoje zapytanie powinno załatwić sprawę:

select * from v_emp where emp_id=&eno;

11
W niektórych przypadkach nastąpi duża poprawa wydajności, gdy jest to WHEREdla tabeli, zamiast WHEREdla widoku.
Doug_Ivison,

Chociaż to, co mówi Doug, jest w pewnym stopniu prawdą, nowoczesne bazy danych mogą wykonać niezwykłą robotę, inteligentnie „rozszerzając” widok i skutecznie kończąc z takim samym wynikiem, jak w przypadku ręcznego wykonywania całego zapytania. Nie zakładaj więc, że będzie to nieefektywne, ponieważ baza danych może Cię zaskoczyć - spójrz na wygenerowany plan zapytań. Godnym uwagi wyjątkiem byłoby, gdyby widok miał klauzulę GROUP BY, która wpływa na dane wyjściowe - w takim przypadku nie można wykonać WHERE z „zewnątrz”.
Simon_Weaver,

8

Hackim sposobem na zrobienie tego bez procedur składowanych lub funkcji byłoby utworzenie tabeli ustawień w bazie danych z kolumnami Id, Param1, Param2 itd. Wstaw wiersz do tej tabeli zawierający wartości Id = 1, Param1 = 0, Param2 = 0 itd. Następnie możesz dodać sprzężenie do tej tabeli w swoim widoku, aby uzyskać pożądany efekt, i zaktualizować tabelę ustawień przed uruchomieniem widoku. Jeśli masz wielu użytkowników aktualizujących tabelę ustawień i uruchamiających widok jednocześnie, rzeczy mogą pójść nie tak, ale poza tym powinno działać poprawnie. Coś jak:

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

byłoby okropnie użyć go jako prośby o wyświetlenie. Ale jest to naprawdę użyteczne, jako konfiguracja / etap / środowisko, aby używać takich ukrytych parametrów. Plus dla mnie za to.
TPAKTOPA

6

Nie. jeśli musisz użyć funkcji zdefiniowanej przez użytkownika, do której możesz przekazać parametry.



5

Widok to nic innego jak predefiniowana instrukcja „SELECT”. Tak więc jedyną prawdziwą odpowiedzią byłoby: Nie, nie możesz.

Myślę, że to, co naprawdę chcesz zrobić, to utworzyć procedurę składowaną, w której w zasadzie możesz użyć dowolnego prawidłowego kodu SQL, aby zrobić, co chcesz, w tym zaakceptować parametry i wybrać dane.

Wydaje się prawdopodobne, że naprawdę wystarczy dodać klauzulę WHERE tylko podczas wybierania z widoku, ale tak naprawdę nie podałeś wystarczająco dużo szczegółów, aby mieć pewność.


5

możemy napisać procedurę składowaną z parametrami wejściowymi, a następnie użyć tej procedury składowanej, aby uzyskać zestaw wyników z widoku. patrz przykład poniżej.

procedura składowana to

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

a widok, z którego możemy uzyskać zbiór wyników, to

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

Jak wiem, widok może być czymś w rodzaju polecenia wyboru. Możesz również dodać parametry do tego wyboru, na przykład w przypadku takich instrukcji:

 WHERE  (exam_id = @var)

4

Nie, widok jest statyczny. Jedną rzeczą, którą możesz zrobić (w zależności od wersji serwera SQl), jest zindeksowanie widoku.

W Twoim przykładzie (odpytywanie tylko jednej tabeli) widok indeksowany nie daje korzyści z prostego wysyłania zapytań do tabeli z indeksem, ale jeśli wykonujesz wiele sprzężeń w tabelach z warunkami łączenia, widok indeksowany może znacznie poprawić wydajność.


4

Jeśli nie chcesz używać funkcji, możesz użyć czegoś takiego

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

Mam nadzieję, że to pomoże



2

Oto opcja, której do tej pory nie widziałem:

Po prostu dodaj kolumnę, którą chcesz ograniczyć do widoku:

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

Możesz ominąć tylko po to, aby uruchomić widok, SQL będzie płakać i płakać, ale po prostu zrób to i uruchom! Nie możesz uratować.

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

1

Twój widok może odnosić się do zewnętrznej tabeli zawierającej twoje parametry.

Jak wspominali inni, widok w SQL Server nie może mieć zewnętrznych parametrów wejściowych. Możesz jednak łatwo sfałszować zmienną w swoim widoku za pomocą CTE. Możesz go przetestować w swojej wersji 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

wydajność:

status  name
12      dbo
0       db_accessadmin
0       db_securityadmin
0       db_ddladmin

również za pośrednictwem 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

również za pośrednictwem 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
Powinno (PL / SQL i T-SQL są podobne pod wieloma względami), ale jest więcej niż jeden sposób, aby się tego dowiedzieć :) Spróbuj.
Oleg Melnikov

0

Mam pomysł, którego jeszcze nie próbowałem. Możesz to zrobić:

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)

Twoje parametry zostaną zapisane i zmienione w tabeli konfiguracji.


2
Jeśli masz wątpliwości co do prawdziwości odpowiedzi, nie publikuj jej przed sprawdzeniem, czy jest to przynajmniej adekwatne rozwiązanie. W obecnej postaci jest to bardziej pytanie niż odpowiedź.
chb,

Jednym z problemów z tym rozwiązaniem będzie to, że jeśli zapytanie jest uruchamiane w wielu sesjach, mogą zostać użyte nieprawidłowe dane w tabeli konfiguracji
User1010

0

Zrealizowałem to zadanie dla moich potrzeb w następujący sposób

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

0

Po prostu użyj tego widoku do procedury składowanej z wymaganymi parametrami (np. W SQL Server) i wartościami parametrów w widoku zapytań.

Utwórz procedurę składowaną za pomocą View / table: _spCallViewWithParameters

wprowadź opis obrazu tutaj

Wykonaj procedurę:

wprowadź opis obrazu tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.