传入一大串Guids

Posted

技术标签:

【中文标题】传入一大串Guids【英文标题】:Passing in a large string of Guids 【发布时间】:2013-08-15 20:51:13 【问题描述】:

我有一个有 2 个问题的存储过程。

    它有 40 个参数。我知道第一条评论将是重新设计我的存储过程,因此它没有 40 个参数。但是,这是一个具有大标准部分的搜索表单。因此,用户为搜索指定了多达 40 个不同的标准。然后我们将这些值作为参数传入。现在我有一个 40 个参数的 sproc。将这些作为 XML 参数传入并在内部或表参数中解析是否更有效(我们仍在运行 SQL 2k5,但正在考虑升级到 2k12)。

    我的三个参数是由引号和逗号分隔的长字符串 Guid 值。基本上,用户会看到一个产品线列表,有时有数百个。然后,他们单击要搜索的那些。我们限制了他们可以检查的行数,只是因为字符串太长,但是我们传递了一长串用引号和逗号分隔的 Guid。我知道这不是正确的做法。像这样传入数组或 Guid 值集合的标准 Trans SQl 模式是什么?我有 40 个独立字段中的 3 个这样做。我们希望更有效地做到这一点,并且能够超过我们目前的限制。

【问题讨论】:

A k 保存两个 0?很确定您可以将 TVP 传递给存储过程。 表值参数为 SQL2008+ 这个链接提供了 2008 年之前的选项,但没有一个看起来那么吸引人msdn.microsoft.com/en-us/library/bb675163(v=vs.90).aspx 2012 年的另一个原因。 我真的很想使用 TVP,但恐怕这个项目的交付日期远远早于我们测试和迁移到 2k12 所需的时间。 字符串怎么会“太长”?你知道varchar(max) 支持超过 20 亿个字符,对吧?有很多 GUID。 【参考方案1】:

SQL Server 2005

在您可以利用表值参数之前,我建议您只创建一个专门的表值 UDF 来拆分您的 GUID 参数。然后,您可以在连接、存在子句、交叉应用等中使用输出。

CREATE FUNCTION dbo.SplitGUIDs
(
   @List       VARCHAR(MAX),
   @Delimiter  VARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT [GUID] = CONVERT(UNIQUEIDENTIFIER, x) FROM
      ( 
        SELECT x = RTRIM(y.i.value('.[1]', 'nvarchar(4000)'))
        FROM 
        ( 
          SELECT x = CONVERT(XML, '<i>' 
            + REPLACE(@List, @Delimiter, '</i><i>') 
            + '</i>').query('.')
        ) AS a CROSS APPLY x.nodes('i') AS y(i)
      ) AS x WHERE LEN(Item) > 0
   );
GO

用法:

DECLARE @GUIDs VARCHAR(MAX);

SET @GUIDs = 'E2072E08-84D3-4EEA-A6ED-F38F1E4E34A6,'
           + 'A6B047BA-647E-4B35-8D95-F4A204B860F6';

SELECT [GUID] FROM dbo.SplitGUIDs(@GUIDs, ',') AS g;

结果:

Item
------------------------------------
E2072E08-84D3-4EEA-A6ED-F38F1E4E34A6
A6B047BA-647E-4B35-8D95-F4A204B860F6

存储过程可能如下所示:

CREATE PROCEDURE dbo.Whatever
  @GUIDs VARCHAR(MAX)
AS 
BEGIN
  SET NOCOUNT ON;

  SELECT t.columns
    FROM dbo.sometable AS t
    INNER JOIN dbo.SplitGUIDs(@GUIDs, ',') AS g
    ON t.key = g.[GUID];
END
GO

(当然,如果您的字符串中的任何元素包含无效的 GUID,该函数将失败。在 SQL Server 2012 中,您可以使用 TRY_CONVERT(),但您不需要这样做,因为您将使用 TVP ,更多下文。)

SQL Server 2008+

稍后,当您从 SQL Server 2005 毕业时(以及其他面临此问题但使用 SQL Server 2008+ 的读者),您可以使用表类型更有效地做到这一点:

CREATE TYPE dbo.GUIDs AS TABLE(GUID UNIQUEIDENTIFIER PRIMARY KEY);

那么你的存储过程可以把这个类型作为输入而不是一个大字符串:

CREATE PROCEDURE dbo.Whatever
  @GUIDs dbo.GUIDs READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT t.columns
    FROM dbo.sometable AS t
    INNER JOIN @GUIDs AS g
    ON t.key = g.[GUID];
END
GO

(请注意切换到 TVP 是多么容易 - 只需更改第 2 行和第 9 行。)

然后,您的 Web 应用程序可以将 DataTable 等集合传递给 @GUIDs 参数。没有混乱的字符串拆分,没有类型转换,没有人为限制您可以传递多少个不同的 GUID。

【讨论】:

您上面的解决方案实际上就是我们的做法。我们将 GUIDS 列表作为一个巨大的字符串传递,然后我们将该字符串传递到一个 UDF,该 UDF 返回一个填充了这些 Guid 的表。然后我们将该表连接到存储过程中的主 FROM 语句。 但是我们正在为 3 组值和 3 个额外的表执行此操作,其中包含大量的字符串操作。我正在设法优化这一点。 @GUIDs1, @GUIDs2, @GUIDs3 也有吗?也许你和我对“优化”有不同的看法——如果你的查询的其余部分很复杂,你如何处理 GUID 将无法解决这个问题。【参考方案2】:

在我看来,您试图通过一个存储过程实现太多目标。 我会尝试将搜索算法或查询分解成更小的部分,并将特定搜索实体的特定结果整理到结果集中。

可能的替代方案:

在应用程序中动态编写 SQL 查询。 将您的程序分解为多个较小的程序,并仅根据搜索条件调用必要的程序

在我看来,在大多数情况下,必须(手动)在 T-SQL 中解析 CSV 是代码/架构的味道。

编辑:请阅读...http://technet.microsoft.com/en-US/library/ms187926(v=sql.90).aspx

一个存储过程最多可以有 2,100 个参数。

那么问题是什么?我以为您出于某种原因遇到了限制。 只是使用更多的参数,它比解析 CSV 更可取。 也不要忘记您可以为存储的 proc 参数提供默认值,这样您就不必在每次调用时提供所有 40 多个参数,只要它们被命名发送。

也不要忘记表值 UDF...它们非常好,因为它们可以以下列方式使用

select * from dbo.fn_ProductsByLine('Toasters') pbl inner join dbo.fn_ProductsByPrice(10,20) pbp on pbl.productID = pbp.productID

【讨论】:

我已经尝试将其分解为多个查询,并且在某种程度上我们有,但这是我们最大的一个。基本上,我们有一个产品目录,从电子产品到体育用品再到珠宝等。有大量领域可供搜索,我们仅限于所有人共同的领域。但是,当您将它们全部加起来时,您会发现这是一个很大的可能性列表,并不是很容易减少。 例如:Price From, Price To, Margin Price From, margin Price To, 名称/描述的文本搜索, 原产国, 制造国, Is Closeout, Is Green, Is USA Only,是否可以直接发货,有高分辨率图像,产品创建日期范围和产品最新更新日期,直接发货天数范围,然后列出他们想要搜索的每个行、类别和产品 ID。 我确实看到了您的困难,我很感激我的回答需要在数据库范围之外进行更改。您是否考虑过用位图替换布尔字段? 对每个 GUID 使用单独参数的问题是,如果业务规则人员决定,您必须对过程的接口、主体本身、数据访问层等进行认真的修改现在您需要支持 50、75 或 500 个 GUID。加入拆分函数将使以后转换为 TVP 变得更加容易。

以上是关于传入一大串Guids的主要内容,如果未能解决你的问题,请参考以下文章

Ajax的post表单,不在url后接一大串参数键值对的方法

同步通信和异步通信

rocket linux 部署

8.Docker之使用dockerfile创建nginx镜像

子串算法建议

java.lang.IllegalArgumentException的使用场景