2017-04-12 72 views
0

我正在使用SQL Server 2008 R2版本。我有一個簡單的表格,它有多列。 EmpId其中一列的類型爲nvarchar(50)SQL Server:與多個值合併

我在寫一個存儲過程,其中我接收到一個可以具有以下值之一的輸入。

  1. 單EMPID:'12345'
  2. 多EMPID的逗號分隔:'12345, 56789, 98987'
  3. null

我想要什麼:

  1. 如果EMPID是單EMPID只是返回

    select * 
    from table_name 
    where EmpId = @empId 
    
  2. 如果EMPID是用逗號分隔值,只返回

    select * 
    from table_name 
    where EmpId in (select * from dbo.splitstring(@empId)) 
    
  3. 如果EMPID爲null,只返回

    Select * 
    from table_name 
    

    無需where子句。

涵蓋所有三個條件,這是我努力:

DECLARE @empId nvarchar(2000) 

SET @empId = '97050001,97050003, 97050004' 

SELECT TOP 10 empId 
FROM Employee 
WHERE empId in (COALESCE((select * from dbo.splitstring(@empId)),[empId])) 

我收到以下錯誤:

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

我理解錯誤。 COALESCE()正期待一個值,但是當我得到逗號分隔值時,splitstring函數會返回多個值。

我不希望建立一個動態查詢,所以除了用的if else塊複製代碼,我檢查,如果EMPID是空運行select * from table_name其他運行select * from table name where empId in()。我有什麼選擇?

以逗號分隔字符串分割成表,我使用這個功能:

CREATE FUNCTION dbo.splitstring (@stringToSplit VARCHAR(MAX)) 
RETURNS 
    @returnList TABLE ([Name] [nvarchar] (500)) 
AS 
BEGIN 
    DECLARE @name NVARCHAR(255) 
    DECLARE @pos INT 

    WHILE CHARINDEX(',', @stringToSplit) > 0 
    BEGIN 
     SELECT @pos = CHARINDEX(',', @stringToSplit) 
     SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) 

     INSERT INTO @returnList 
      SELECT @name 

     SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)[email protected]) 
    END 

    INSERT INTO @returnList 
     SELECT @stringToSplit 
    RETURN 
END 
+1

其中@empId爲空 或由於OP想要檢索的所有數據,同時通過'NULL' EMPID在(SELECT * FROM dbo.splitstring(@empId))' –

+0

@juergend爲什麼不使用'SELECT * FROM TABLE_NAME 。 –

+0

@ahmedab​​delqader:這就是我的查詢所做的 –

回答

1

多一點複雜的版本分裂串機能的研究的嘗試。

CREATE PROCEDURE myProc 
@EmpId NVARCHAR(50) = NULL 
AS 
BEGIN 
    SET NOCOUNT ON; 

DECLARE @Sql NVARCHAR(MAX); 

SET @Sql = N' SELECT * FROM Table_Name WHERE 1 = 1' 
     + CASE WHEN @EmpId IS NOT NULL THEN 
      N' AND empId IN (SELECT Split.a.value(''.'', ''VARCHAR(100)'') empId 
           FROM (
             SELECT Cast (''<X>'' 
                + Replace(@EmpId, '','', ''</X><X>'') 
                + ''</X>'' AS XML) AS Data 
           ) AS t CROSS APPLY Data.nodes (''/X'') AS Split(a) 
          ) ' ELSE N'' END 

Exec sp_executesql @Sql 
        ,N'@EmpId NVARCHAR(50)' 
        ,@EmpId 

END 

這個版本將有更好的表現,因爲

  1. 更高效的字符串分割。
  2. 由於sp_executesql的參數化執行計劃緩存,所以執行計劃更好。
+0

僅供參考,這可以在沒有動態SQL的情況下完成。 – tep

+0

@tylerparsons,我知道它可以在沒有動態SQL的情況下完成,我已經看到了你的答案,這是處理這種操作的一種不好的方式。使用或不使用動態SQL不是目標,但編寫一個查詢可幫助查詢優化器提供最佳執行計劃,這是一個優先事項,不幸的是,您的答案根本就沒有這樣做。 –

+0

這是一個很好的觀點,我仔細檢查了我的查詢的執行計劃,它看起來像是同樣的計劃,即不管@empId是否爲null,總是會使用「splitstring」,這可能並不理想。只是好奇 - 將兩個查詢非動態地放在if else結構中會更快嗎?我查了一下,也會爲每個案例使用正確的執行計劃。 – tep

1

取決於你所需要的: -

I do not want to build dynamic query, so besides duplicating the code with if else block where I check if empId is null run select * from table_name else run select * from table name where empId in()

,並避免重複,使用下一個方法: -

DECLARE @empId nvarchar(2000) 
set @empId = '97050001,97050003,97050004' 
if CHARINDEX(',',@empId) > 0 -- multiple Values 
begin 
    set @empId = '''' + replace (@empId,',',''',''') + '''' 
end 
else if @empId is null 
begin 
    set @empId = 'select empId from Employee' 
end 

exec ('select top 10 empId from Employee where empId in (' + @empId + ')') 

這種方法處理的三種情況: -

  1. 傳遞一個值。
  2. 傳遞多個值。
  3. 傳遞Null
0

案例1和2都可以對你已經爲案例2.編寫的代碼來處理你只需要添加一個OR條件案例3.

select * 
from table_name 
where @empId is NULL 
    or EmpId in (select * from dbo.splitstring(@empId)) 

話雖這麼說,IN條款包裝SELECT語句,原因如下

  • 他們是慢於加入時,select語句返回多行一般不好的做法
  • 加入更地道

在你的情況,如果只有通過splitstring返回它可能不會有太大的區別了幾行,但下面將這種查詢的一個更通用的方法。

select * 
from table_name t 
left join dbo.splitstring(@empId) s 
    on t.EmpId = s.Name 
where @empId is NULL 
    or s.Name is not NULL 

隨時檢查執行計劃的查詢和配置文件,看看哪一個更快,雖然你最初的實現應該罰款。引用唐納德克努特的話:「不成熟的優化是萬惡之源。」

更新

經過雙重檢查的情況下使用時@empId爲空和非空,它看起來像上面的查詢將始終使用相同的執行計劃,即加入對內容的執行計劃無論@empId是否爲空,in子句。正如@ m-ali所指出的,這可能並不理想。

爲確保正確執行計劃在每種情況下,可以單獨到這個兩個查詢:

IF @empId is NULL 
    select * 
    from table_name 
ELSE 
    select * 
    from table_name 
    where EmpId in (select * from dbo.splitstring(@empId)) 

我已經驗證了在SSMS兩種情況下,正確的執行計劃。

聲明:我還沒有分析它,但@ m-ali建議的字符串拆分也可能更快。