2009-12-30 41 views
12

OK, câu hỏi cột có điều kiện thứ mười một:Làm cách nào để có điều kiện lọc trên cột trong mệnh đề WHERE?

Tôi đang viết một tệp được lưu trữ có tham số đầu vào được ánh xạ tới một trong một số cột cờ. Cách tốt nhất để lọc cột được yêu cầu là gì? Tôi hiện đang trên SQL2000, nhưng về để di chuyển đến SQL2008, vì vậy tôi sẽ có một giải pháp đương đại nếu có sẵn.

Bảng truy vấn trong sproc trông giống như

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 

và tôi muốn làm một cái gì đó giống như

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

vì vậy nếu tôi gọi sproc như exec uspMyProc @flag = 'foo' Tôi muốn nhận lại hàng 1 và 4

Tôi biết tôi không thể thực hiện một phần trong việc chia sẻ trực tiếp trong SQL. Để thực hiện SQL động, tôi sẽ phải đặt toàn bộ truy vấn vào một chuỗi, nối chuỗi param @flag trong mệnh đề WHERE và sau đó thực thi chuỗi. Ngoài cảm giác bẩn thỉu mà tôi nhận được khi thực hiện SQL động, truy vấn của tôi khá lớn (tôi chọn một vài chục trường, tham gia 5 bảng, gọi một vài hàm), vì vậy nó là một chuỗi khổng lồ lớn vì một dòng duy nhất trong bộ lọc WHERE 3 dòng.

Cách khác, tôi có thể có 4 bản sao của truy vấn và chọn trong số chúng trong câu lệnh CASE. Điều này để lại mã SQL trực tiếp thực thi (và tùy thuộc vào cú pháp hilighting, vv) nhưng với chi phí lặp lại các khối lớn mã, vì tôi không thể sử dụng CASE chỉ với mệnh đề WHERE.

Có tùy chọn nào khác không? Bất kỳ tham gia khôn lanh hoặc hoạt động hợp lý có thể được áp dụng? Hoặc tôi nên vượt qua nó và thực hiện SQL động?

+1

"exec uspMyProc @flag = 'foo' Tôi muốn quay lại hàng 1." Tại sao chỉ có hàng 1 và cũng không phải hàng 4? –

+0

@Mark - d'oh, vâng, đã được sửa! – Val

Trả lời

18

Có một số cách để thực hiện việc này:

Bạn có thể thực hiện việc này với tuyên bố trường hợp.

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

Bạn có thể sử dụng 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 

.... 

Bạn có thể có một mệnh đề phức tạp ở đâu với nhiều dấu ngoặc đơn.

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

Bạn có thể làm điều này với sql động:

DECLARE @SQL nvarchar(4000) 

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

EXECUTE sp_ExecuteSQL @SQL, N'' 

Có hơn, nhưng tôi nghĩ rằng một trong những sẽ giúp bạn có được đi.

+0

+1 - Cá nhân tôi sử dụng cách tiếp cận 'CASE' vì tôi nghĩ nó là gọn gàng nhất, mặc dù tôi có thể sử dụng một trường hợp đơn giản hơn là một trường hợp được tìm kiếm như bạn đã chỉ ra. tức là 'CASE @flag WHEN 'foo' THÌ fooFlag KHI 'bar' rồi barFlag END = 1') –

+0

@Greg: Tất nhiên, đó là khả năng có hiệu suất tồi tệ nhất trong tất cả các tùy chọn, vì bạn hiện không chỉ gói một cột trong một hàm, bạn sắp xếp tất cả chúng. Nếu bạn có một chỉ mục khác nhau trên mỗi cột thì điều này sẽ trở thành một bảng quét. – Aaronaught

+0

+1 - tùy thuộc vào độ phức tạp của truy vấn. Mặc dù trong kinh nghiệm của tôi ngay cả khi truy vấn bắt đầu đơn giản nó luôn luôn kết thúc (trong quá trình bảo trì vv) phức tạp, và tôi ước tôi đã bắt đầu với sql động. – Russell

4

"Cách khác, tôi có thể có 4 bản sao của truy vấn và chọn trong số chúng trong câu lệnh CASE."

Bạn không cần phải sao chép toàn bộ truy vấn của bạn 4 lần, chỉ cần thêm tất cả các khả năng vào các điều khoản ở đâu trong bản duy nhất của bạn trong những truy vấn:

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

+1: Đó sẽ là cách tiếp cận của tôi, mặc dù tôi vẫn sử dụng động nên tất cả các OR không bị kéo dọc theo mỗi lần truy vấn chạy. –

+0

vâng, đây là giải pháp ưa thích của tôi. Tôi đã chọn câu trả lời của Gabriel, tho, vì anh ấy đã cho tôi rất nhiều lựa chọn, bao gồm cả. cái này. – Val

0

Bạn có thể có một tham số cho mỗi lá cờ có thể cột, sau đó kiểm tra xem tham số là null hay giá trị trong cột bằng tham số. Sau đó, bạn vượt qua trong một 1 cho những lá cờ mà bạn muốn kiểm tra và để lại những người khác null.

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

Thành thật mà nói, điều này dường như là một ứng cử viên lý tưởng để xây dựng truy vấn LINQ động và bỏ qua SPROC khi bạn truy cập SQL2008.

+0

Đây là những gì tôi sẽ làm nếu hiệu suất không phải là mối quan tâm chính (tức là các cột cờ không được lập chỉ mục). – Aaronaught

+0

Không, tôi thực sự muốn tránh nhiều thông số - bảo trì nhiều hơn nếu khi tôi mở rộng sang nhiều cờ hơn. Với một tham số duy nhất, việc bảo trì bị hạn chế đối với SP; nếu tôi thêm thông số, tôi cũng phải thay đổi cấu trúc của mã gọi, thay vì chỉ chuyển các giá trị mới trong một tham số hiện có. Cảm ơn, tho! – Val

+0

Ồ, vâng - quên đề cập đến, tho tôi đang chuyển DB sang SQL2008, ứng dụng của tôi là ASP Classic và tôi sẽ không có cơ hội chuyển phần này của ứng dụng sang .NET bất kỳ lúc nào. Vì vậy, LINQ là ra ... – Val

3

Điều tôi sẽ làm là CASE một số biến ở đầu. Ví dụ:

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

Những điều tốt về truy vấn này là, vì các giá trị có thể cho "cờ" được bao bọc, bạn có thể tính toán tất cả các điều kiện của bạn như là điều kiện tiên quyết thay vì quấn cột trong họ. Điều này đảm bảo chỉ mục hiệu suất cao tìm kiếm trên bất kỳ cột nào được lập chỉ mục và không yêu cầu viết bất kỳ SQL động nào. Và tốt hơn là viết 4 truy vấn riêng biệt vì những lý do rõ ràng.

+0

Điều này doesnt đảm bảo một chỉ số hiệu suất cao tìm kiếm vì nó sẽ đánh giá lại barFlag> = ISNULL (@barFlag, 0) mỗi lần. – Russell

+0

@barFlag chỉ là biến vô hướng, không phải cột bảng.Đó là sargable - bạn có thể mở rộng toàn bộ biểu thức thành 8 cờ liên tục riêng biệt. Hãy thử nó và xem. – Aaronaught

+0

Xin lỗi, Aaron, tôi đã sử dụng truy vấn với các biểu thức IS NULL OR .. :) Đánh giá các biểu thức số không xuất hiện để làm điều đó. Xin lỗi – Russell

0
where 
    case when @value<>0 then Field else 1 end 
    = 
    case when @value<>0 then @value else 1 end 
+1

Thêm một số lời giải thích văn bản xung quanh mã của bạn sẽ giúp người hỏi. – RBT

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

select * from EmployeeChatTbl chat 

    where chat.ConversationDetails like '%'[email protected]+'%' 

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

làm việc

khi companyID là hiện nay, sau đó lọc dựa trên nó được thực hiện, khác khôn ngoan, lọc được bỏ qua.

Các vấn đề liên quan