0

我在 C#.net 中有一个对象列表(从几个文本文件创建),我需要将其存储在 SQL2005 数据库文件中。不幸的是,表值参数从 SQL2008 开始,所以它们无济于事。我从 MSDN 发现一种方法是“将多个数据值捆绑到分隔字符串或 XML 文档中,然后将这些文本值传递给过程或语句”,但我对存储过程相当陌生,需要更多帮助。我知道我可以创建一个存储过程来创建一条记录,然后遍历我的列表并添加它们,但这就是我想要避免的。谢谢。

Input file example (Other files contain pricing and availability):
Matnr   ShortDescription    LongDescription ManufPartNo Manufacturer    ManufacturerGlobalDescr GTIN    ProdFamilyID    ProdFamily  ProdClassID ProdClass   ProdSubClassID  ProdSubClass    ArticleCreationDate CNETavailable   CNETid  ListPrice   Weight  Length  Width   Heigth  NoReturn    MayRequireAuthorization EndUserInformation  FreightPolicyException
10000000    A&D ENGINEERING SMALL ADULT CUFF FOR UA-767PBT  UA-279  A&D ENGINEERING A&D ENG 093764011542    GENERAL General TDINTERNL   TD Internal TDINTERNL   TD Internal 2012-05-13 12:18:43 N       18.000  .350                N   N   N   N
10000001    A&D ENGINEERING MEDIUM ADULT CUFF FOR UA-767PBT UA-280  A&D ENGINEERING A&D ENG 093764046070    GENERAL General TDINTERNL   TD Internal TDINTERNL   TD Internal 2012-05-13 12:18:43 N       18.000  .450                N   N   N   N

一些数据库文件字段:

EffectiveDate           varchar(50)
MfgName                 varchar(500)
MfgPartNbr              varchar(500)
Cost                    varchar(200)
QtyOnHand               varchar(200)
4

3 回答 3

2

您可以很容易地从单个字符串中拆分多个值。假设您可以像这样捆绑字符串,使用逗号分隔“列”,使用分号分隔“行”:

foo, 20120101, 26; bar, 20120612, 32

(这假设冒号和分号不能自然地出现在数据中;如果可以,您需要选择其他分隔符。)

您可以像这样构建一个拆分例程,其中包括一个输出列,允许您确定值在原始字符串中出现的顺序:

CREATE FUNCTION dbo.SplitStrings
(
    @List       NVARCHAR(MAX),
    @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
AS
    RETURN (SELECT Number = ROW_NUMBER() OVER (ORDER BY Number),
        Item FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(@List, Number, 
        CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number)))
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
        FROM sys.all_objects) AS n(Number)
    WHERE Number <= CONVERT(INT, LEN(@List))
        AND SUBSTRING(@Delimiter + @List, Number, 1) = @Delimiter
    ) AS y);
GO

然后你可以像这样查询它(为简单起见,我只处理 3 个属性,但你可以将其推断为 11 或 n):

DECLARE @x NVARCHAR(MAX); -- a parameter to your stored procedure

SET @x = N'foo, 20120101, 26; bar, 20120612, 32';

;WITH x AS 
(
    SELECT ID = s.Number, InnerID = y.Number, y.Item 
    -- parameter and "row" delimiter here:
    FROM dbo.SplitStrings(@x, ';') AS s
    -- output and "column" delimiter here:
    CROSS APPLY dbo.SplitStrings(s.Item, ',') AS y
)
SELECT 
    prop1 = x.Item, 
    prop2 = x2.Item, 
    prop3 = x3.Item
FROM x 
INNER JOIN x AS x2 
ON x.InnerID = x2.InnerID - 1
AND x.ID = x2.ID
INNER JOIN x AS x3
ON x2.InnerID = x3.InnerID - 1
AND x2.ID = x3.ID
WHERE x.InnerID = 1
ORDER BY x.ID;

结果:

prop1   prop2     prop3
------  --------  -------
foo     20120101  26
bar     20120612  32
于 2012-06-11T19:45:58.880 回答
0

我们使用这样的 XML 数据类型...

declare @contentXML xml
set @contentXML=convert(xml,N'<ROOT><V a="124694"/><V a="124699"/><V a="124701"/></ROOT>')

SELECT  content_id,
  FROM  dbo.table c WITH (nolock) 
  JOIN @contentXML.nodes('/ROOT/V') AS R ( v ) ON c.content_id = R.v.value('@a', 'INT')

这是调用存储过程时的样子......

DbCommand dbCommand = database.GetStoredProcCommand("MyStroredProcedure);
database.AddInParameter(dbCommand, "dataPubXML", DbType.Xml, dataPublicationXml);

CREATE PROC dbo.usp_get_object_content
(
    @contentXML XML
)
AS 
BEGIN
    SET NOCOUNT ON

    SELECT  content_id,
      FROM    dbo.tblIVContent c WITH (nolock) 
      JOIN @contentXML.nodes('/ROOT/V') AS R ( v ) ON c.content_id = R.v.value('@a', 'INT')

 END

SQL Server 解析 XML 的速度不是很快,因此使用 SplitStrings 函数可能会更高效。只是想提供一个替代方案。

于 2012-06-11T20:04:44.623 回答
0

我能想到几个选项,但当我输入其中一个(拆分选项)时,上面的@Bertrand 先生发布了。唯一的问题是 SQL不擅长字符串操作

因此,另一种选择是使用您的 sproc 假定将存在的 #Temp 表。构建动态SQL,效果如下:

使用您需要的形状开始一个事务,CREATE TABLE #InsertData然后循环您要插入的数据,使用INSERT INTO #InsertData SELECT <values> UNION ALL SELECT <values>....

这种方法有一些限制,其中之一是随着数据集变得非常大,您可能需要将 INSERT 拆分为批次。(我不记得我自己学习时遇到的具体错误,但是对于很长的值列表,我有 SQL 抱怨。)不过,解决方案很简单:只需生成一系列具有较少数量的 INSERT每行。例如,您可能会INSERT SELECTs用 1000做 10 次,UNION ALLs而不是INSERT SELECT用 10000次做 1 次UNION ALLs。您仍然可以将整个批处理作为单个命令的一部分传递。

这样做的优点(尽管有各种缺点——使用临时表、长命令字符串等)是它将所有字符串处理卸载到等式中更高效的 C# 端,并且不需要额外的持久数据库对象(Split 函数;尽管如此,有时谁不需要其中之一)?

如果您使用 Split() 函数,我鼓励您将其卸载到 SQLCLR 函数,而不是 T-SQL UDF(出于上面链接所示的性能原因)。

最后,无论您选择哪种方法,请注意,如果您的数据可以包含包含分隔符的字符串,您将遇到更多问题(例如,在 Aaron 的回答中,如果数据是:

'I pity the foo!', 20120101, 26; 'bar, I say, bar!', 20120612, 32

同样,因为 C# 在字符串处理方面比 T-SQL 更好,所以最好不要使用 T-SQL UDF 来处理它。

编辑

对于动态 INSERT 选项,请注意以下要考虑的附加点。

您需要确定此处的任何输入是否是潜在危险输入,是否需要在使用前进行清洁。你不能轻易地参数化这些数据,所以这是一个重要的数据。在我使用这种策略的地方,我已经对数据的类型有了强有力的保证(特别是,我用它来为一个表播种一个要处理的整数 ID 列表,所以我迭代的是整数而不是任意的,不受信任的字符串)。如果您没有类似的保证,请注意 SQL 注入的危险。

于 2012-06-11T20:08:20.287 回答