處理返回多行的case語句子查詢
select * from temp tp join a on tp.id = a.id where ( a.enddate between @startdate and @endDate or a.startdate between @startdate and @endDate) and a.id1 in (case when (isnull(@id,''<>'') then (select distinct id from #temp2) else a.id1 end)
#temp2 查詢返回多行。是否可以通過 case 語句來實現,否則請盡可能採用最佳方式。
UDPATE:我使用 case 語句,因為當@id 為空時它應該返回所有記錄(a.id1=1,2,3,4,5,6)但是當@id=1,2,3 是不為空,它應該只返回那些具有 a.id1=1,2,3 的記錄。
注意#1:在不知道您使用的是哪個 RDMBS 的情況下,我不能說您提出的查詢是否有效(我傾向於“懷疑”)。
注意#2:我假設不應該
(
在isnull
那裡,否則我不確定您要實現什麼邏輯,即您需要使用匹配的括號更新您的查詢。我想你會想要將
isnull
測試拉到頂層並使用 anor
來滿足你的條件測試,例如:-- using '@id in (select ...)' construct select * from temp tp join a on tp.id = a.id where ( a.enddate between @startdate and @endDate or a.startdate between @startdate and @endDate) and ( @id is not NULL or (@id is NULL and a.id1 in (select distinct id from #temp2) ) ) -- or -- using 'exists (select ...)' construct select * from temp tp join a on tp.id = a.id where ( a.enddate between @startdate and @endDate or a.startdate between @startdate and @endDate) and ( @id is not NULL or (@id is NULL and exists(select 1 from #temp2 t where t.id=a.id1) ) )
如果我理解正確,您有一個變數,該變數
@id
將包含一個以逗號分隔的 ID 值列表,即 string<>
,或者為 NULL。如果它具有 ID 值,則您要搜尋的行必須具有指定的 ID 之一;否則,您要搜尋的行必須與 table 中的 ID 之一匹配#temp2
。不幸的是,您不能將字元串常量用作
IN
直接比較的值列表。從這一點出發,基本上有兩種方法。您可以將語句建構為動態 SQL 語句(在 ID 列表中連接),或者從字元串中解析出各個 ID,將它們載入到表中,然後使用它。我們將使用動態 SQL,因為我認為它更容易遵循:
DECLARE @stmt NVARCHAR(max); SET @stmt = N'SELECT * FROM temp tp INNER JOIN a ON (tp.id = a.id) WHERE ( a.enddate BETWEEN @startdate AND @enddate or a.startdate BETWEEN @startdate AND @enddate ) and a.id1 in (' + CASE WHEN ISNULL(@id,'<>') = '<>' THEN N'SELECT DISTINCT id FROM #temp2' ELSE @id END + ');' ; EXECUTE sp_executesql @stmt ,N'@startdate datetime, @enddate datetime' ,@startdate = @startdate ,@enddate = @enddate;
使用
sp_executesql
儲存過程時,需要給它一個語句來執行;您還可以給它一個參數列表,然後分配這些參數的值。在這裡,我為參數使用了相同的名稱,以及我們將用來填充它們的變數。對於語句和參數列表,sp_executesql
需要nvarchar
類型字元串;這就是為什麼字元串常量以 . 開頭的原因N'
。我們建構的語句是硬編碼的(帶有開始和結束日期的參數),除了
IN
列表中的實際內容。你的CASE
聲明在那裡,如果你有一個明確的 ID 列表,如果你沒有,則放入SELECT
from#temp2
。更新:如果您確實對動態 SQL 解決方案有疑問,那麼您可以使用儲存過程、函式或 CLR 將逗號分隔的整數值列表拆分為臨時表。有很多方法可以做到這一點:這裡有一篇文章的連結,討論了一些選項,並提供了推薦的解決方案。還有很多其他的 - 鍵入
"SQL Server" split string
,您將獲得大約 507,000 個結果。忽略任何談論STRING_SPLIT
功能從 SQL Server 2016 開始可用,當然,這不是您的版本。鑑於您的字元串可能不會太長,任何 2016 年之前的解決方案都可能對您足夠好。如果您的 ID 列表可能增長到 1,000 多個項目,您可能需要考慮仔細查看 Aaron 的文章(上面的連結)並查看哪些選項仍然可以合理執行。如果您使用
SplitInts_IOnline_2
Aaron 部落格文章中的函式:CREATE FUNCTION dbo.SplitInts_Inline_2 ( @List VARCHAR(MAX), @Delimiter CHAR(1) ) RETURNS TABLE AS RETURN ( SELECT Item = CONVERT(INT, (SUBSTRING( @Delimiter + @List + @Delimiter, w.n + 1, CHARINDEX(@Delimiter, @Delimiter + @List + @Delimiter, w.n + 1) - w.n - 1 ))) FROM ( SELECT n = v0.n + v1.n + v2.n + v3.n FROM ( SELECT n = 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 ) AS v0, ( SELECT n = 0 UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48 UNION ALL SELECT 64 UNION ALL SELECT 80 UNION ALL SELECT 96 UNION ALL SELECT 112 UNION ALL SELECT 128 UNION ALL SELECT 144 UNION ALL SELECT 160 UNION ALL SELECT 176 UNION ALL SELECT 192 UNION ALL SELECT 208 UNION ALL SELECT 224 UNION ALL SELECT 240 ) AS v1, ( SELECT n = 0 UNION ALL SELECT 256 UNION ALL SELECT 512 UNION ALL SELECT 768 UNION ALL SELECT 1024 UNION ALL SELECT 1280 UNION ALL SELECT 1536 UNION ALL SELECT 1792 UNION ALL SELECT 2048 UNION ALL SELECT 2304 UNION ALL SELECT 2560 UNION ALL SELECT 2816 UNION ALL SELECT 3072 UNION ALL SELECT 3328 UNION ALL SELECT 3584 UNION ALL SELECT 3840 ) AS v2, ( SELECT n = 0 UNION ALL SELECT 4096 UNION ALL SELECT 8192 UNION ALL SELECT 12288 UNION ALL SELECT 16384 UNION ALL SELECT 20480 UNION ALL SELECT 24576 UNION ALL SELECT 28672 UNION ALL SELECT 32768 UNION ALL SELECT 36864 UNION ALL SELECT 40960 UNION ALL SELECT 45056 UNION ALL SELECT 49152 UNION ALL SELECT 53248 UNION ALL SELECT 57344 UNION ALL SELECT 61440 UNION ALL SELECT 65536 UNION ALL SELECT 69632 UNION ALL SELECT 73728 UNION ALL SELECT 77824 UNION ALL SELECT 81920 UNION ALL SELECT 86016 UNION ALL SELECT 90112 UNION ALL SELECT 94208 UNION ALL SELECT 98304 UNION ALL SELECT 102400 UNION ALL SELECT 106496 UNION ALL SELECT 110592 UNION ALL SELECT 114688 UNION ALL SELECT 118784 UNION ALL SELECT 122880 UNION ALL SELECT 126976 UNION ALL SELECT 131072 UNION ALL SELECT 135168 UNION ALL SELECT 139264 UNION ALL SELECT 143360 UNION ALL SELECT 147456 ) v3 ) w WHERE w.n = CHARINDEX(@Delimiter, @Delimiter + @List + @Delimiter, w.n) AND w.n < LEN(@Delimiter + @List) );
然後您的程式碼將如下所示:
IF ISNULL(@id,'<>' = '<>') BEGIN SELECT tp.*, a.* FROM temp tp INNER JOIN a ON (tp.id = a.id) WHERE ( a.enddate BETWEEN @startdate AND @enddate or a.startdate BETWEEN @startdate AND @enddate ) AND EXISTS (SELECT 1 FROM #temp2 WHERE is = a.id1) END ELSE BEGIN SELECT tp.*, a.* FROM temp tp INNER JOIN a ON (tp.id = a.id) INNER JOIN dbo.SplitInts_Inline_2(@id,',') ids ON (a.id1 = ids.Item) WHERE ( a.enddate BETWEEN @startdate AND @enddate or a.startdate BETWEEN @startdate AND @enddate ) END;
請注意,這假設您的列表中沒有重複的值。我在函式中看不到任何重複數據刪除,因此要消除重複,您必須將上述函式的 iiner 連接替換為:
INNER JOIN (SELECT DISTINCT Item FROM dbo.SplitInts_Inline_2(@id,',')) ids ON (a.id1 = ids.Item)
另請注意,如果該表中沒有重複項,則
EXISTS
檢查#temp2
也可以是INNER JOIN
to 。a
我這樣做是EXISTS
為了確保我們只獲得 中的行的一個副本a
,即使 中存在多個匹配的行#temp2
。就像我說的,我認為動態 SQL 選項更容易遵循。