2009-11-17 18 views
10

chcę użyć parametru dla zapytania tak:Parametry w zapytaniu z wyrażeniem w?

SELECT * FROM MATABLE 
WHERE MT_ID IN (368134, 181956) 

więc myślę o tym

SELECT * FROM MATABLE 
WHERE MT_ID IN (:MYPARAM) 

ale to nie działa ...

Czy istnieje sposób, aby zrobić to?

I faktycznie korzysta IBX i Firebird 2.1

ja nie wiem ile parametry klauzula.

+3

AFAIK SQL, który nie pozwala parametry klauzul. Niektóre obejścia działają, zobacz inne odpowiedzi, ale bądź świadomy ryzyka wstrzyknięcia SQL. –

+1

Ostatnio próbowałem zrobić to samo z MS SQL Server i to też nie działa. –

Odpowiedz

5

Skończyło się na używaniu globalnej tabeli tymczasowej w Firebird, najpierw wstawiając wartości parametrów i do pobierania wyników Używam zwykłego JOIN zamiast klauzuli WHERE ... IN. Tabela tymczasowa jest specyficzna dla transakcji i rozliczana przy zatwierdzeniu (ON COMMIT DELETE ROWS).

4

Może powinieneś wite to tak:

SELECT * FROM MATABLE 
WHERE MT_ID IN (:MYPARAM1 , :MYPARAM2) 
+0

To powinno zadziałać, ale jeśli jego lista filtrów nie jest ustalona, ​​będzie musiał skonstruować każdy warunek indywidualnie. – yozey

+0

Ja już to robię, ale muszę ręcznie utworzyć SQL, który zależy od tego, ile potrzebuję parametrów. Chcę po prostu przez SQL –

0

Jeśli używasz Oracle, to powinno się sprawdzić Tom Kyte na blogu na dokładnie ten temat (link).

Po ołowiu pana Kyte, oto przykład:

SELECT * 
    FROM MATABLE 
WHERE MT_ID IN 
     (SELECT TRIM(substr(text, instr(text, sep, 1, LEVEL) + 1, 
          instr(text, sep, 1, LEVEL + 1) - 
          instr(text, sep, 1, LEVEL) - 1)) AS token 
      FROM (SELECT sep, sep || :myparam || sep AS text 
        FROM (SELECT ',' AS sep 
          FROM dual)) 
     CONNECT BY LEVEL <= length(text) - length(REPLACE(text, sep, '')) - 1) 

Gdzie byś wiążą :MYPARAM do '368134,181956' w Twoim przypadku.

+0

Tryb Oracle nie działają dla Firebird, ale wydaje mi się, że to jest to, czego potrzebuję –

1

Odpowiedź z yurish to rozwiązanie w dwóch z trzech przypadków:

  • jeśli masz ograniczoną liczbę elementów, które można dodać do swojej w klauzuli
  • lub, jeśli są chętni do tworzenia parametry w locie dla każdego potrzebnego elementu (nie znasz liczby elementów w czasie projektowania)

Ale jeśli chcesz mieć dowolną liczbę elementów, a niekiedy żadnych elementów, możesz wygenerować Polecenie SLQ w locie. Korzystanie z formatu pomaga.

+0

Tak Chcę dowolnej liczby elementów i nie chcę generować instrukcji SQL. –

+0

Nie sądzę, że to możliwe, ale żałuję, że się mylę. Chciałem to zrobić i sam postanowiłem napisać SQL. Przygotowane zapytania mają sens tylko w silniku DB, który wie, czego się spodziewać (ile parametrów, a także ich typy). Nawet gdyby istniał sposób na parametryzację dowolnej liczby wejść, myślę, że nie byłoby wzrostu wydajności w porównaniu do "zaskakującego" silnika DB ze świeżym zapytaniem za każdym razem. – Mihaela

+0

Używanie parametrów nie tylko przy zwiększaniu wydajności wymaga ochrony przed atakami typu SQL injection. Albo poświęcasz dużo wysiłku na odkażanie ciągów parametrów wejściowych, albo traktujesz je jak parametry. – mghie

3

Nie sądzę, że jest to coś, co można zrobić. Czy istnieje jakiś szczególny powód, dla którego nie chcesz samodzielnie tworzyć zapytania?

Użyłem tej metody kilka razy, ale nie używa ona parametrów. Używa łańcucha znaków i jego właściwości DelimitedText. Stworzysz listę IDList i zapełnisz ją swoimi ID.

Query.SQL.Add(Format('MT_ID IN (%s)', [IDList.DelimitedText])); 
+0

Co jest nie tak z tym? Zobacz http://stackoverflow.com/questions/332365/xkcd-sql-injection-please-explain – mghie

+1

@mghie, tak jest okropnie źle, jeśli mówimy o wprowadzaniu danych przez użytkownika, ale założyłem, że ponieważ jest to lista Identyfikatory to nie jest coś, co użytkownik dostarczy. W tym przypadku spodziewałem się kodów produktów, numerów faktur itd. Być może popełniłem błąd i dziękuję za poprawę mojej odpowiedzi. – johnny

+0

Możesz mieć rację, że w tym konkretnym przypadku wstrzyknięcie SQL może nie być możliwe, ale jest to realne zagrożenie i wydaje się być tak trudne dla ludzi, aby zrozumieć/zapamiętać, że uważam, że należy być konsekwentnym i nie robić takich rzeczy, w ogóle . – mghie

0

Oto technika, z której korzystałem w przeszłości, aby obejść problem z instrukcją "IN".Tworzy listę "OR" na podstawie ilości wartości określonych za pomocą parametrów (unikalnych). Następnie wystarczyło dodać parametry w kolejności, w jakiej pojawiły się na dostarczonej liście wartości.

var 
    FilterValues: TStringList; 
    i: Integer; 
    FilterList: String; 
    Values: String; 
    FieldName: String; 
begin 
    Query.SQL.Text := 'SELECT * FROM table WHERE '; // set base sql 
    FieldName := 'some_id'; // field to filter on 
    Values := '1,4,97'; // list of supplied values in delimited format 
    FilterList := ''; 
    FilterValues := TStringList.Create; // will get the supplied values so we can loop 
    try 
    FilterValues.CommaText := Values; 

    for i := 0 to FilterValues.Count - 1 do 
    begin 
     if FilterList = '' then 
     FilterList := Format('%s=:param%u', [FieldName, i]) // build the filter list 
     else 
     FilterList := Format('%s OR %s=:param%u', [FilterList, FieldName, i]); // and an OR 
    end; 
    Query.SQL.Text := Query.SQL.Text + FilterList; // append the OR list to the base sql 

    // ShowMessage(FilterList); // see what the list looks like. 
    if Query.ParamCount <> FilterValues.Count then 
     raise Exception.Create('Param count and Value count differs.'); // check to make sure the supplied values have parameters built for them 

    for i := 0 to FilterValues.Count - 1 do 
    begin 
     Query.Params[i].Value := FilterValues[i]; // now add the values 
    end; 

    Query.Open; 
finally 
    FilterValues.Free; 
end; 

Mam nadzieję, że to pomoże.

+0

zrobiłem to już w jednym projekcie, ale moim celem było bez kodu paskowego –

+0

To byłoby bardzo trudne. Od lat mam z tym problemy. Jeśli masz komponenty obsługujące makra, możesz to zrobić, ale używając parametrów, które wątpię w to i po stronie serwera, nie udało mi się tego dokonać. – yozey

2

Parametry są symbolami zastępczymi dla pojedynczych wartości, co oznacza, że ​​klauzula IN, która akceptuje rozdzielaną przecinkami listę wartości, nie może być używana z parametrami.

Pomyśl o tym w ten sposób: gdziekolwiek umieszczę wartość, mogę użyć parametru.

Więc w klauzuli jak: IN (: param)

mogę powiązać zmiennej wartości, ale tylko 1 wartość, np: IN (4)

Teraz, jeśli wziąć pod uwagę "Wyrażenie wartości wyrażenia IN", otrzymujesz ciąg wartości: IN (1, 4, 6) -> to 3 wartości z przecinkami między nimi. Jest to część łańcucha SQL, a nie część wartości, dlatego nie można jej powiązać z parametrem.

Oczywiście nie tego chcesz, ale jest to jedyne możliwe z parametrami.

9

Dla kogo wciąż jest zainteresowany. Zrobiłem to w Firebird 2.5, używając innej procedury zainspirowanej tym postem.

How to split comma separated string inside stored procedure?

CREATE OR ALTER PROCEDURE SPLIT_STRING (
    ainput varchar(8192)) 
RETURNS (
    result varchar(255)) 
AS 
DECLARE variable lastpos integer; 
DECLARE variable nextpos integer; 
DECLARE variable tempstr varchar(8192); 
BEGIN 
    AINPUT = :AINPUT || ','; 
    LASTPOS = 1; 
    NEXTPOS = position(',', :AINPUT, LASTPOS); 
    WHILE (:NEXTPOS > 1) do 
    BEGIN 
    TEMPSTR = substring(:AINPUT from :LASTPOS for :NEXTPOS - :LASTPOS); 

    RESULT = :TEMPSTR; 
    LASTPOS = :NEXTPOS + 1; 
    NEXTPOS = position(',', :AINPUT, LASTPOS); 
    suspend; 
    END 

END 

Po przejechaniu SP następującą listę

CommaSeperatedList = 1,2,3,4

i nazywają

SELECT * FROM SPLIT_STRING(:CommaSeperatedList) 

wynik będzie :

RESULT 
1 
2 
3 
4 

i mogą być wykorzystane w następujący sposób:

SELECT * FROM MyTable where MyKeyField in (SELECT * FROM SPLIT_STRING(:CommaSeperatedList)) 
+0

Jeśli potrzebujesz reasult jako liczbę całkowitą, zmieniasz typ wyjściowy z varchar (255) na liczbę całkowitą i zastępujesz 'RESULT =: TEMPSTR;' tym wynikiem 'RESULT = cast (: TEMPSTR as integer);'. –

0

Jest jeszcze jedna sztuczka używać odwróconej SQL LIKE warunek.

przekazać listę jako ciąg (VARCHAR) parametr jak '~12~23~46~567~'

Następnie u mają zapytanie jak where ... :List_Param LIKE ('%~' || CAST(NumField AS VARCHAR(20)) || '~%')

0
CREATE PROCEDURE TRY_LIST (PARAM_LIST VARCHAR(255)) RETURNS (FIELD1....) 
AS 
BEGIN 
/* Check if :PARAM_LIST begins with colon "," and ands with colon "," 
    the list should look like this --> eg. **",1,3,4,66,778,33,"**   
    if the format of list is right then GO if not just add then colons 
*/ 
IF (NOT SUBSTRING(:PARAM_LIST FROM 1 FOR 1)=',') THEN PARAM_LIST=','||PARAM_LIST; 
IF (NOT SUBSTRING(:PARAM_LIST FROM CHAR_LENGTH(:PARAM_LIST) FOR 1)=',') THEN PARAM_LIST=PARAM_LIST||','; 

/* Now you are shure thet :PARAM_LIST format is correct */ 
/* NOW ! */ 
FOR SELECT * FROM MY_TABLE WHERE POSITION(','||MY_FIELD||',' in :PARAM_LIST)>0 
INTO :FIELD1, :FIELD2 etc... DO 
BEGIN 
    SUSPEND; 
END 

END 

How to use it. 

SELECT * FROM TRY_LIST('3,4,544,87,66,23') 
or SELECT * FROM TRY_LIST(',3,4,544,87,66,23,') 
if the list have to be longer then 255 characters then just change the part of header f.eg. like PARAM_LIST VARCHAR(4000) 
1

select * from MATABLE GDZIE MT_ID IN (: myparam) zamiast korzystania myparam z :, użyj nazwy parametru.

jak SELECT * Z MATABLE GDZIE MT_ID w (wybierz REGEXP_SUBSTR (** myparam '[^;] +', 1, poziom) od Podwójnej połączyć się REGEXP_SUBSTR (myparam „[^ ,] +”, 1, poziom) nie jest NULL)) **

MYPARAM- '368134,181956'

Powiązane problemy