如何給存儲過程,傳一個數組參數?


接到這個需求,本以為簡單。誰知道SQL不支持數組。於是想用','分割傳進去,哪知道SQL居然沒有split()函數,還得用substring & charindex,坑爹啊。

方法一 分割

例:通過SQL Server存儲過程傳送數組參數刪除多條記錄

eg. ID 值為'1,2,3' 以下存儲過程就是刪除表中id號為1,2,3的記錄:

CREATE PROCEDURE DeleteNews 
    @ID nvarchar(500) 
as 
    DECLARE @PointerPrev int 
    DECLARE @PointerCurr int 
    DECLARE @TId int 
    Set @PointerPrev=1 
     
    while (@PointerPrev < LEN(@ID)) 
    Begin 
        Set @PointerCurr=CharIndex(',',@ID,@PointerPrev) 
        if(@PointerCurr>0) 
        Begin 
            set @TId=cast(SUBSTRING(@ID,@PointerPrev,@PointerCurr-@PointerPrev) as int) 
            Delete from News where 
ID=@TID 
            SET @PointerPrev = @PointerCurr+1 
        End 
        else 
            Break 
    End 
    --刪除最后一個,因為最后一個后面沒有逗號,所以在循環中跳出,需另外再刪除 
     set @TId=cast(SUBSTRING(@ID,@PointerPrev,LEN(@ID)-@PointerPrev+1) as int) 
     Delete from News where 
ID=@TID 
GO

 

這個方法麻煩不?於是又有另外一種方法——臨時表

 

方法二 Table對象

 

傳3個參數,都是數組形式還有時間類型用存儲過程更新

 

@Oid = 1,2,3,4

 

@Did = 111,222,333,444

 

@DateArr = '2007-1-1,2007-1-2,2007-1-3,2007-1-4'

 

 

 

CREATE proc Test999

 

@Oid nvarchar(1000)    --ID1

 

,@Did nvarchar(1000)   --ID2

 

,@DateArr nvarchar(1000) --日期

 

AS

 

DECLARE @id1s varchar(8000), @id2s varchar(8000), @dates varchar(8000)

 

set @id1s=@Oid       

 

set @id2s=@Did       

 

set @dates = @DateArr

 

-- 調用函數實現處理

 

SELECT @id1s=@id1s, @id2s=@id2s,@dates = @dates

 

 

 

UPDATE A SET terminate_time = B.dt

 

FROM [Table] A,(

 

SELECT

 

    id1 = CONVERT(int, Desk_id.value),

 

    id2 = CONVERT(int, room_id.value),

 

    dt = CONVERT(datetime, terminate_time.value)

 

FROM dbo.f_splitstr(@id1s) Desk_id, dbo.f_splitstr(@id2s) room_id, dbo.f_splitstr(@dates) terminate_time

 

WHERE Desk_id.id = room_id.id

 

    AND Desk_id.id = terminate_time.id

 

) B

 

WHERE A.Desk_id = B.ID1 AND A.room_id = B.ID2

 

GO這個還用到一個函數f_splitstr

 

CREATE FUNCTION dbo.f_splitstr(

 

    @str varchar(8000)

 

)RETURNS @r TABLE(id int IDENTITY(1, 1), value varchar(5000))

 

AS

 

BEGIN

 

    DECLARE @pos int

 

    SET @pos = CHARINDEX(',', @str)

 

    WHILE @pos > 0

 

    BEGIN

 

        INSERT @r(value) VALUES(LEFT(@str, @pos - 1))

 

        SELECT

 

            @str = STUFF(@str, 1, @pos, ''),

 

            @pos = CHARINDEX(',', @str)

 

    END

 

    IF @str > ''

 

        INSERT @r(value) VALUES(@str)

 

    RETURN

 

END

這個方法更加可怕~~~輾轉百度,找到了一個還不錯的方法,用OPENXML,這個SQL2000就支持了。

 

方法三 xml

 

 

 

應該用SQL2000 OpenXML更簡單,效率更高,代碼更可讀:

 

CREATE Procedure [dbo].[ProductListUpdateSpecialList] 

@ProductId_Array NVARCHAR(2000), 
@ModuleId INT 
)

 

AS

 

delete from ProductListSpecial where ModuleId=@ModuleId

 

-- If empty, return 
IF (@ProductId_Array IS NULL OR LEN(LTRIM(RTRIM(@ProductId_Array))) = 0) 
RETURN

 

DECLARE @idoc int

 

EXEC sp_xml_preparedocument @idoc OUTPUT, @ProductId_Array

 

Insert into ProductListSpecial (ModuleId,ProductId) 
Select 
@ModuleId,C.[ProductId] 
FROM 
OPENXML(@idoc, '/Products/Product', 3) 
with (ProductId int ) as C 
where 
C.[ProductId] is not null

 

EXEC sp_xml_removedocument @idoc

哇,看起來還是很復雜的說。有木有更好的辦法呢?

既然是OPENXML,為啥不用XML呢?於是查到,SQL2005以上都支持XML。Good,找到一片天了。

利用SQL2005的XML/XQuery功能,可以很方便的解決傳數組參數的問題。

 

 

declare @xml xml
set @xml = '<?xml version="1.0"?>
<ArrayOfInt>
<int>1</int>
<int>2</int>
<int>3</int>
</ArrayOfInt>'

 

select N.value( '(text())[1]','int' ) RoomId from @xml.nodes('/ArrayOfInt/int') V(N)

結果就是

 

注:上面的數據類型為XML

這樣就可以給存儲過程傳一個集合了,一般是數組,比如主鍵的集合。然后可用通過主鍵集合來查詢記錄。

客戶端可用使用序列化,把list轉化成xml。不過在序列化過程中,遇到了一些小麻煩。

1. .net默認是utf-16,SQL只認識utf-8

2. 出現很討厭的xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"

可以用我這個類

    public static class SerializeHelper
    {
        private static readonly XmlSerializerNamespaces Namespaces = new XmlSerializerNamespaces();

        static SerializeHelper()
        {
            //去掉 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            Namespaces.Add(string.Empty, string.Empty);
        }

        public static string SerializeXml<T>(T obj)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (MemoryStream stream = new MemoryStream())
            {
                serializer.Serialize(stream, obj, Namespaces);
                return Encoding.UTF8.GetString(stream.ToArray());
            }
        }

        public static T DeserializeXml<T>(string obj)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (StringReader reader = new StringReader(obj))
            {
                return (T)serializer.Deserialize(reader);
            }
        }
    }

SQL與XML,XQuery結合起來,功能會很強大的。

從這個事件也看出,我應該多關注SQL各個版本之間新增功能,比如SQL2008新增功能。否則遇到問題,找不到比較好的解決方案。

OK,問題以完美的方式解決了,用最妙的方法解決問題,好開森哦~~~O(∩_∩)O!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM