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;
arunachalam
źródło
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)
Alex Bagnolini
źródło
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
)
Gavin
źródło
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 ”).

Thorsten Dittmar
źródło
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).

Łukasz Szozda
źródło
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;
Mahesh
źródło
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
Bozonik
źródło
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.

Mladen Prajdic
źródło
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ść.

Kris
źródło
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%')  
sunil
źródło
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)
Reza Ameri
źródło
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ść.

Jan
źródło
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

Adnan Badar
źródło
3

nie, możesz przekazać parametr do wyświetlanej procedury

aicuxiao
źródło
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)
FarajDaoud
źródło
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);
Kentonbmax
źródło
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
Oleg Melnikov
źródło
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.

Emman
źródło
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
cretalex
źródło
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

Bhuwan Maharjan
źródło