2009-06-23 19 views
6

Tôi có một truy vấn SQL cơ bản, bắt đầu với:Làm cách nào để sử dụng danh sách giá trị được phân tách bằng dấu phẩy làm bộ lọc trong T-SQL?

SELECT top 20 application_id, [name], location_id FROM apps 

Bây giờ, tôi muốn hoàn thành nó để nó thực hiện điều này (viết bằng Mã giả)

if @lid > 0 then 
    WHERE location_id IN (@lid) 
else 
    WHERE location_id is all values in location_id column 

Theo yêu cầu, đây là một ví dụ:

application_id    name    location_id 
---------------------------------------------------------- 
1       Joe Blogs   33 
2       Sam Smith   234 
3       Jeremy Carr  33 

@locid là kết quả được đưa ra bởi người dùng, ví dụ ' 33, 234 '

Nếu @ lid rỗng thì tôi muốn nó xuất tất cả các hàng cho location_id với tên và application_id. Nếu không, tôi muốn nó ra tất cả các hàng liên quan đến những con số được cung cấp trong @lid (viết tắt của location_id

Vì vậy, nếu @lid là 0:.

application_id    name    location_id 
---------------------------------------------------------- 
1       Joe Blogs   33 
2       Sam Smith   234 
3       Jeremy Carr  33 

Ngược lại, nếu @lid chứa '33'

application_id    name    location_id 
---------------------------------------------------------- 
1       Joe Blogs   33 
3       Jeremy Carr  33 
+0

Ông có thể vui lòng cung cấp một số dữ liệu mẫu và resultset mong muốn? – Quassnoi

+0

Hy vọng điều này là đủ cho bạn. Cảm ơn trước. –

Trả lời

3

Hãy thử sử dụng Trường hợp, phục vụ mục đích của một nhà điều hành IIF hoặc một toán tử bậc ba. Vui lòng kiểm tra liên kết này http://msdn.microsoft.com/en-us/library/ms181765.aspx

cổ vũ

+0

Tôi đã thử trường hợp, nhưng với kết quả hỗn hợp. Bạn có thể cung cấp một số mã mẫu mà tôi có thể thử chống lại các ví dụ của tôi không? –

+0

CASE WHEN MyNum <10 THEN 'Nhỏ' ELSE 'Big' END –

2

Xem cụm từ này trong blog của tôi:

Nếu bạn @lid là danh sách dấu phẩy phân cách các số nguyên, sử dụng này:

WITH cd AS 
     (
     SELECT 1 AS first, CHARINDEX(',', @lid, 1) AS next 
     UNION ALL 
     SELECT next + 1, CHARINDEX(',', @lid, next + 1) 
     FROM cd 
     WHERE next > 0 
     ), 
     lid AS 
     (
     SELECT CAST(SUBSTRING(@lid, first, CASE next WHEN 0 THEN LEN(@lid) + 1 ELSE next END - first)AS INT) AS id 
     FROM cd 
     ) 
SELECT d.* 
FROM (
     SELECT DISTINCT id 
     FROM lid 
     ) l 
JOIN apps a 
ON  a.location_id = l.id 
     AND @lid <> '0' 
UNION ALL 
SELECT * 
FROM apps a 
WHERE @lid = '0' 

Đây là hiệu quả hơn so với sử dụng OR cấu trúc.

+0

Mục đích của tôi là trả về tất cả các hàng trong cột location_id nếu không có gì trong @lid, do đó, cùng một loại kết quả là "SELECT location_id từ các truy vấn" sẽ tạo ra. Nếu không có gì trong @lid, tôi muốn nó tìm kiếm mọi location_id trong cơ sở dữ liệu. –

+0

Đã xóa câu trả lời của tôi và cho bạn +1 sau một số điểm chuẩn trên cấu trúc OR với một số tập dữ liệu lớn hơn. Không bao giờ khá bấm vào tôi như thế nào không hiệu quả họ (chỉ được sử dụng trên các tập dữ liệu nhỏ hơn). Dù sao, tôi nghĩ rằng các truy vấn cuối cùng là những gì OP đang tìm kiếm. – Eric

+0

@Eric: Cảm ơn. Có, SQL Server không thể tối ưu hóa OR tốt. Tuy nhiên, truy vấn của bạn sẽ hoạt động tốt trong MySQL: nó sẽ nhận thấy rằng @ lid không phải là số không và loại bỏ hoàn toàn vị từ kế hoạch. – Quassnoi

0

Bạn không thực sự cần các nhà điều hành ternary cho việc này:

SELECT top 20 application_id, [name], location_id 
FROM apps 
WHERE (@lid > 0 AND location_id IN (@lid)) OR @lid <= 0 
+0

Làm thế nào "location_id IN (@lid)" hoạt động mà không có SQL động? – gbn

+0

Tôi mặc dù @lid là một INT? –

+0

Có bản cập nhật câu hỏi trong đó CSV bây giờ là ... – gbn

0

Bạn đang đặt câu hỏi khác nhau ở đây. Đây là câu trả lời cho câu hỏi ban đầu của bạn (khoảng các nhà điều hành ternary, nhưng như bạn thấy đấy, bạn không cần bất cứ điều gì giống như một nhà điều hành ternary cho việc này):

SELECT top 20 application_id, [name], location_id 
FROM apps 
WHERE @lid = 0 OR location_id IN (@lid) 

Nhưng đó là trước khi chúng ta biết rằng @lid là một varchar và có thể chứa các giá trị được phân cách bằng dấu phẩy khác nhau.

Vâng, đây (khoảng csv) là một câu hỏi đã được hỏi ở đây trước:

Passing an "in" list via stored procedure
T-SQL stored procedure that accepts multiple Id values

+0

Làm cách nào để "location_id IN (@lid)" hoạt động mà không có SQL động? – gbn

+0

@gbn: điều này không liên quan gì đến câu hỏi trong tay, đó là về một toán tử bậc ba (cũng không phải là những gì cần thiết ở đây, nhưng đó lại là một cách khác); – fretje

3

Nếu @locid là danh sách ví dụ: "33, 234" vv, sau đó không giải pháp ở đây sẽ hoạt động. Tuy nhiên, tôi đoán chúng đã được đăng trước khi bạn cập nhật thông tin này.

Tôi cho rằng vì bạn nói điều này:

@locid là kết quả do người dùng cung , ví dụ '33, 234'

Bạn không thể mở rộng biến trực tiếp để rằng location_in IN (33, 234). Bạn đang thực sự yêu cầu location_id = '33, 234', điều này sẽ không thành công với chuyển đổi CAST, vì ưu tiên datatype.

Bạn phải phân tích cú pháp danh sách trước tiên thành biểu mẫu bảng để sử dụng trong cấu trúc JOIN/EXISTS. Có một số tùy chọn và Erland bao gồm tất cả ở đây: Arrays and Lists in SQL Server 2005

+0

Bạn là đúng, và tôi đã phân tích cú pháp này "varchar" danh sách để nó đã kiểm tra tất cả các giá trị, như là một hình thức sử dụng nhiều giá trị trong một lựa chọn tuyên bố. –

0

Sau đây nên thực hiện thủ thuật cho bạn.

DECLARE @lid SMALLINT

SET @lid = 0

CHỌN top 20 APPLICATION_ID, [name], location_id

TỪ ứng dụng

WHERE ((@lid> 0 VÀ location_id = @lid)

OR (@lid = 0 AND location_id > @lid)) 

Nếu @ lid = 0 thì nó sẽ trả về TẤT CẢ hàng. IF @lid có một giá trị cụ thể, chỉ có hàng cho giá trị @lid đó được trả lại.

0

Mặc dù đó không phải là cách thực hành tốt nhất để sử dụng 1 = 1, nhưng mẹo này ở đây.

SELECT top 20 application_id, [name], location_id FROM apps 
WHERE 
    (@lid > 0 and @lid = location_id) 
or 
    (isnull(@lid, 0) <= 0 and 1=1) 
1

Một cách là chia chuỗi trên dấu phẩy, loại bỏ các khoảng trắng và chèn giá trị vào bảng tạm thời. Sau đó, bạn có thể tham gia truy vấn của bạn với bảng tạm thời.

Đây là đoạn mã T-SQL phân tách danh sách được phân cách bằng dấu phẩy và chèn các thành viên vào bảng tạm thời. Khi bạn đã điền bảng, bạn có thể tham gia chống lại nó.

-- This bit splits up a comma separated list of key columns 
-- and inserts them in order into a table. 
-- 
if object_id ('tempdb..#KeyCols') is not null 
    drop table #KeyCols 

create table #KeyCols (
     ,KeyCol   nvarchar (100) 
) 

set @comma_pos = 0 
set @len = len(@KeyCols) 
while @len > 0 begin 
    set @comma_pos = charindex(',', @KeyCols) 
    if @comma_pos = 0 begin 
     set @KeyCol = @KeyCols 
     set @KeyCols = '' 
    end else begin 
     set @KeyCol = left (@KeyCols, @comma_pos - 1) 
     set @KeyCols = substring(@KeyCols, 
           @comma_pos + 1, 
           len (@KeyCols) - @comma_pos) 
    end 
    insert #KeyCols (KeyCol) 
    values (@KeyCol) 
    set @len = len (@KeyCols) 
end 
+0

+1, một kỹ thuật hữu ích. Bất kỳ lý do gì để không đi với một bảng tạm thời trong bộ nhớ chỉ? – CRice

+0

Không đặc biệt - nếu tôi nhớ lại chính xác sproc mà ban đầu đến từ cuộc sống đã nêu trên SQL Server 2000. Bạn có thể sử dụng một biến bảng dễ dàng như trên các phiên bản SQL Server hiện đại. – ConcernedOfTunbridgeWells

+0

Ok, tốt để biết. Tôi thích sự thay thế trong bộ nhớ. – CRice

2

Giả sử @locid & @lid là cùng, tôi sẽ sử dụng @locid ... Sau đây sẽ làm việc. Tôi đã chia nó ra để giữ cho nó trông đẹp trên SO.

SELECT application_id, [name], location_id 
FROM apps 
WHERE 
    ( 
    @locid = 0 
    OR 
    CHARINDEX 
    ( 
     ',' + CAST(location_id AS VARCHAR(50)) + ',' 
     , 
     ',' + @locid + ',' 
     , 
     0 
    ) > 0 
) 
+0

làm cách nào tôi có thể cắt thêm không gian được chuyển vào CSV? –

2
WHERE CHARINDEX(LocationId, @lid) > 1 
+0

Bạn nên xây dựng dựa trên câu trả lời của mình tốt hơn. –

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