2009-10-06 14 views
14

Chcę napisać procedurę przechowywaną SQL Server 2005, która wybierze i zwróci rekordy użytkownika z tabeli użytkownika dla niektórych użytkowników, którzy zostaną przekazani do procedury składowanej jako parametr.SQL: w klauzuli w procedurze przechowywanej: jak przekazywać wartości

Jak to zrobić?

Mogę przekazać identyfikatory użytkowników jako ciąg oddzielony przecinkiem. Tak, aby można było korzystać z różnych sposobów, np. : Chcę wybrać rekordy dla identyfikatora 5,6,7,8,9

Jak napisać procedurę przechowywaną?

+0

To jest częste pytanie. Mam opcję tutaj: http://stackoverflow.com/questions/977021/can-a-stored-procedure-have-dynamic-parameters-to-be-used-in-an-in-clause/977114#977114 – ScottE

Odpowiedz

17

Dla SQL Server 2005, sprawdź Erland Sommarskog za doskonałą Arrays and Lists in SQL Server 2005 artykuł, który pokazuje kilka technik, w jaki sposób zajmuje się listami i tablicami w SQL Server 2005 (ma też inny artykuł dla SQL Server 2000).

Jeśli można uaktualnić do wersji SQL Server 2008, można korzystać z nowej funkcji o nazwie „tabela wyceniane parametr”:

Najpierw utwórz zdefiniowany przez użytkownika typ tabeli

CREATE TYPE dbo.MyUserIDs AS TABLE (UserID INT NOT NULL) 

drugie, użycie że typ tabeli w procedurze przechowywanej jako parametr:

CREATE PROC proc_GetUsers @UserIDTable MyUserIDs READONLY 
AS 
SELECT * FROM dbo.Users 
    WHERE userid IN (SELECT UserID FROM @UserIDTable) 

Zobacz szczegóły here.

Marc

7

można użyć dynamicznego sql. Przekazać w oświadczeniu SQL SP poprzez zmienną i złączyć je w zapytaniu w SQL i wykonać przy użyciu sp_execute sql

create procedure myproc(@clause varchar(100)) as 
begin 
    exec sp_executesql 'select * from users where userid in (' + @clause +')' 
end 
+8

działa, ale dynamiczny SQL ma swój udział w problemach (bezpieczeństwo, wydajność itp.). –

3

Zakładając, T-SQL, można użyć tej funkcji piękny (która zwraca tabela).

DROP FUNCTION sp_ConvertStringToTable 
GO 
CREATE FUNCTION sp_ConvertStringToTable(@list ntext) 
     RETURNS @tbl TABLE (Position INT IDENTITY(1, 1) NOT NULL, 
          Value INT NOT NULL) AS 
    BEGIN 
     DECLARE @pos  int, 
       @textpos int, 
       @chunklen smallint, 
       @str  nvarchar(4000), 
       @tmpstr nvarchar(4000), 
       @leftover nvarchar(4000) 

     SET @textpos = 1 
     SET @leftover = '' 
     WHILE @textpos <= datalength(@list)/2 
     BEGIN 
     SET @chunklen = 4000 - datalength(@leftover)/2 
     SET @tmpstr = ltrim(@leftover + substring(@list, @textpos, @chunklen)) 
     SET @textpos = @textpos + @chunklen 

     SET @pos = charindex(' ', @tmpstr) 
     WHILE @pos > 0 
     BEGIN 
      SET @str = substring(@tmpstr, 1, @pos - 1) 
      INSERT @tbl (Value) VALUES(convert(int, @str)) 
      SET @tmpstr = ltrim(substring(@tmpstr, @pos + 1, len(@tmpstr))) 
      SET @pos = charindex(' ', @tmpstr) 
     END 

     SET @leftover = @tmpstr 
     END 

     IF ltrim(rtrim(@leftover)) <> '' 
     INSERT @tbl (Value) VALUES(convert(int, @leftover)) 

     RETURN 
    END 
GO 

W ten sposób:

SELECT * FROM Users 
WHERE userid IN 
(SELECT Value FROM sp_ConvertStringToTable('1 2 3')) 

Można zmienić funkcję składowaną pracować z oddzielonych przecinkami ciągi zamiast spacjami nich.

Jeśli nie chcesz/nie możesz użyć zapisanej funkcji, możesz dołączyć jej kod do procedury przechowywanej, jeśli jest taka potrzeba.

EDYCJA: jest niesamowicie bardziej wydajna niż konkatenacja ciągów.

+0

Czy masz statystyki na ten temat? Chciałbym je przeczytać –

+0

to jest powolne, sprawdź to: http://stackoverflow.com/questions/1456192/comparing-a-column-to-a-list-of-values-in-t-sql/1456404 # 1456404 –

+0

Woah! Dziękuję KM, wykonanie kilku testów i zastąpienie go projektem już teraz! W każdym razie moje "to jest szybciej" oznaczało odwoływanie się do dynamicznego sql. –

4

zobaczyć my previous answer to this

jest to najlepsze źródło:

http://www.sommarskog.se/arrays-in-sql.html

utworzyć funkcję podziału i używać go lubię:

SELECT 
    * 
    FROM YourTable y 
    INNER JOIN dbo.splitFunction(@Parameter) s ON y.ID=s.Value 

I prefer the number table approach

Na tej meto d do pracy, trzeba zrobić ten jeden raz konfiguracji tabeli:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO Numbers 
    FROM sys.objects s1 
    CROSS JOIN sys.objects s2 
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) 

Po Numery stół jest skonfigurowana, stworzyć tę funkcję:

CREATE FUNCTION [dbo].[FN_ListToTable] 
(
    @SplitOn char(1)  --REQUIRED, the character to split the @List string on 
    ,@List  varchar(8000)--REQUIRED, the list to split apart 
) 
RETURNS TABLE 
AS 
RETURN 
(

    ---------------- 
    --SINGLE QUERY-- --this will not return empty rows 
    ---------------- 
    SELECT 
     ListValue 
     FROM (SELECT 
        LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue 
        FROM (
          SELECT @SplitOn + @List + @SplitOn AS List2 
         ) AS dt 
         INNER JOIN Numbers n ON n.Number < LEN(dt.List2) 
        WHERE SUBSTRING(List2, number, 1) = @SplitOn 
      ) dt2 
     WHERE ListValue IS NOT NULL AND ListValue!='' 

); 
GO 

Teraz można łatwo podzielić ciąg CSV do stół i dołącz do niego:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,') 

WYJŚCIE:

ListValue 
----------------------- 
1 
2 
3 
4 
5 
6777 

(6 row(s) affected) 

Twój może przejść w ciąg CSV w procedurze i przetwarzać tylko wiersze dla podanych identyfikatorów:

SELECT 
    y.* 
    FROM YourTable y 
     INNER JOIN dbo.FN_ListToTable(',',@GivenCSV) s ON y.ID=s.ListValue 
+0

Przyznaję, że używam tego od kilku lat, ale wróciłem, by ugryźć mnie w tyłek. Chunking wersja z wieloma oświadczeniami (tutaj: http://www.sommarskog.se/arrays-in-sql-2005.html#tblnum) jest rzeczywiście dwukrotnie szybsza. Problem był zauważalny tylko wtedy, gdy zaangażowana tabela zawierała blisko milion wierszy. Weź to pod uwagę, zanim użyjesz rozwiązania @ KM. – pkExec

8

Wystarczy użyć go jak to będzie działać

Create procedure sp_DoctorList 
@userid varchar(100) 
as 
begin 
exec ('select * from doctor where userid in ('+ @userid +')') 
end 
+0

Dzięki za poradę kolega! Po prostu ** exec ** część pracowała dla mnie w mojej procedurze 'wstaw do #temp_hourly exec ('wybierz ZONE_ID ze strefy ZONE gdzie ZONE_ID w (' + @ ZoneId + ') grupy według ZONE_ID')' –

-1

Można również użyć Find_IN_SET zamiast w . Zobacz zapytania poniżej

create procedure myproc(IN in_user_ids varchar(100)) 
begin 
    select * from users where FIND_IN_SET(userid, in_user_ids); 
end 
+0

'FIND_IN_SET' nie jest SQL standardowa funkcja i ** nie ** dostępna w Microsoft ** SQL Server ** (o jaki pytał OP) –

0

spróbować to działa na mnie

DECLARE @InClause NVARCHAR(100) 
SET @InClause = 'tom,dick,harry' 
DECLARE @SafeInClause NVARCHAR(100) 
SET @SafeInClause = ',' + @InClause + ',' 
SELECT * FROM myTable WHERE PATINDEX(',' + myColumn + ',', @SafeInClause) > 0 
Powiązane problemy