传入一大串Guids

时间:2013-08-15 20:51:13

标签: sql sql-server tsql sql-server-2005 query-optimization

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

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

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

2 个答案:

答案 0 :(得分:4)

SQL Server 2005

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

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。

答案 1 :(得分:0)

听起来像是你试图通过一个存储过程实现太多。 我会尝试打破搜索算法或查询较小的peices并将特定搜索实体的特定结果整理到结果集中。

可能的替代方案:

  • 在应用程序中动态编写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