Hvordan betinget filtrere på en kolonne i et WHERE-ledd?

stemmer
13

OK, n'te betinget kolonne spørsmål:

Jeg skriver en lagret proc som tar en inngangsparameter som er tilordnet en av flere flagg kolonner. Hva er den beste måten å filtrere på den forespurte kolonne? Jeg er for tiden på SQL2000, men i ferd med å flytte til SQL2008, så jeg skal ta en moderne løsning hvis en er tilgjengelig.

Tabellen spørres i sproc ser ut

ID ...  fooFlag  barFlag  bazFlag  quuxFlag
--      -------  -------  -------  --------
01         1        0       0          1
02         0        1       0          0
03         0        0       1          1
04         1        0       0          0

og jeg ønsker å gjøre noe sånt

select ID, name, description, ...
from myTable
where (colname like @flag + 'Flag') = 1

så hvis jeg kaller sproc som exec uspMyProc @flag = 'foo'jeg ville komme tilbake rekker 1 og 4.

Jeg vet at jeg ikke kan gjøre den delen i parentesar direkte i SQL. For å gjøre dynamisk SQL, vil jeg måtte stappe hele søket til en streng, sette sammen den @flag param i WHERE-leddet og deretter exec strengen. Bortsett fra den skitne følelsen jeg får når du gjør dynamisk SQL, er søket mitt ganske stor (jeg velge et par dusin felt, bli med 5 bord, ringer et par funksjoner), så det er en stor gigantisk streng alt på grunn av en enkelt linje i en 3-linje der filter.

Alternativt kunne jeg ha 4 eksemplarer av spørringen og velg blant dem i en sak uttalelse. Dette gjør at SQL-kode direkte kjørbar (og under syntaks hilighting, etc.), men på bekostning av å gjenta store biter av kode, siden jeg ikke kan bruke CASE på bare WHERE-leddet.

Er det noen andre alternativer? Enhver vanskelig tiltrer eller logiske operasjoner som kan brukes? Eller skal jeg bare komme over det og exec den dynamiske SQL?

Publisert på 30/12/2009 klokken 00:57
kilden bruker
På andre språk...                            


6 svar

stemmer
20

Det finnes noen måter å gjøre dette:

Du kan gjøre dette med en sak uttalelse.

select ID, name, description, ...
from myTable
where CASE
    WHEN @flag = 'foo' then fooFlag
    WHEN @flag = 'bar' then barFlag
END = 1

Du kan bruke IF.

IF (@flag = 'foo') BEGIN
    select ID, name, description, ...
    from myTable
    where fooFlag = 1
END ELSE IF (@flag = 'bar') BEGIN
    select ID, name, description, ...
    from myTable
    where barFlag = 1
END

....

Du kan ha en komplisert der klausul med mye parentes.

select ID, name, description, ...
from myTable
where (@flag = 'foo' and fooFlag = 1)
OR (@flag = 'bar' and barFlag = 1) OR ...

Du kan gjøre dette med dynamisk sql:

DECLARE @SQL nvarchar(4000)

SELECT @SQL = N'select ID, name, description, ...
from myTable
where (colname like ''' + @flag + 'Flag'') = 1'

EXECUTE sp_ExecuteSQL @SQL, N''

Det er mer, men jeg tror en av disse vil komme i gang.

Svarte 30/12/2009 kl. 01:02
kilden bruker

stemmer
4

"Alternativt kunne jeg ha 4 eksemplarer av spørringen og velg blant dem i en sak uttalelse."

Du trenger ikke å kopiere hele spørringen 4 ganger, bare legge alle mulighetene i Hvor klausuler i ett eksemplar av spørringen:

select ID, name, description, ...
from myTable
where (@flag = 'foo' and fooFlag = 1) OR (@flag = 'bar' and barFlag = 1) OR ...
Svarte 30/12/2009 kl. 01:03
kilden bruker

stemmer
0

Man kan ha en parameter for hvert mulig flagg kolonne, og kontrollere at parameteren er null eller den verdi i kolonnen er lik parameteren. Da passerer du i en 1 for flaggene som du ønsker å sjekke, og la de andre null.

select id, name, description, ...
from myTable
where (@fooFlag is null or fooFlag = @fooFlag) AND
      (@barFlag is null or barFlag = @barFlag) AND
      ...

Ærlig, skjønt, virker dette som en ideell kandidat for å bygge en dynamisk LINQ spørring og hoppe SPROC når du kommer til SQL2008.

Svarte 30/12/2009 kl. 01:06
kilden bruker

stemmer
3

Hva jeg kan gjøre er CASEnoen variabler i begynnelsen. Eksempel:

DECLARE
    @fooFlag int,
    @barFlag int,
    @bazFlag int,
    @quuxFlag int

SET @fooFlag = CASE WHEN @flag = 'foo' THEN 1 ELSE NULL END
SET @barFlag = CASE WHEN @flag = 'bar' THEN 1 ELSE NULL END
SET @bazFlag = CASE WHEN @flag = 'baz' THEN 1 ELSE NULL END
SET @quuxFlag = CASE WHEN @flag = 'quux' THEN 1 ELSE NULL END

SELECT ID, name, description, ...
FROM myTable
WHERE (fooFlag >= ISNULL(@fooFlag, 0) AND fooFlag <= ISNULL(@fooFlag, 1))
AND (barFlag >= ISNULL(@barFlag, 0) AND barFlag <= ISNULL(@barFlag, 1))
AND (bazFlag >= ISNULL(@bazFlag, 0) AND bazFlag <= ISNULL(@bazFlag, 1))
AND (quuxFlag >= ISNULL(@quuxFlag, 0) AND quuxFlag <= ISNULL(@quuxFlag, 1))

Den gode ting om dette spørsmålet er at, fordi de mulige verdier for "flagg" er avgrenset, kan du beregne alle dine conditionals som forutsetninger i stedet for å pakke kolonner i dem. Dette garanterer en høy ytelse index søke på hvilken kolonner er indeksert, og krever ikke skrive noen dynamisk SQL. Og det er bedre enn å skrive 4 separate spørringer for åpenbare grunner.

Svarte 30/12/2009 kl. 01:06
kilden bruker

stemmer
0
where
   case when @value<>0 then Field else 1 end
   =
   case when @value<>0 then @value else 1 end
Svarte 16/08/2016 kl. 09:24
kilden bruker

stemmer
1

int bør aksepteres som varchar verdi

declare @CompanyID as varchar(10) = ''    -- or anyother value

select *  from EmployeeChatTbl chat

  where chat.ConversationDetails like '%'+@searchKey+'%' 

                and
                (
                    (0 = CASE WHEN (@CompanyID = '' ) THEN 0 ELSE 1 END) 
                                or 
                    (chat.CompanyID = @CompanyID)
                )

arbeider

når den companyID er til stede, deretter filtrering basert på dette er gjort, noe annet, filtrering blir hoppet over.

Svarte 06/03/2017 kl. 04:57
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more