如何重写我的查询以便不会多次调用函数?

时间:2012-06-02 16:33:00

标签: sql-server tsql

我正在定期(每月)导入大量数据。在转换过程中,我将字符串拆分为多个列,但这不仅仅是一个简单的拆分。有一些逻辑可以决定字符串的哪个部分进入哪个字段。

我编写了一个内联函数,它将字符串分成多个部分,并为您提供指定索引处的值。

参数是:

  1. 字符串值
  2. 定界符
  3. 索引
  4. 例如:

    如果字符串值为X4-728Z5-121-84gff,并且您希望函数为您提供121,那么您可以像这样调用函数:

    fn_MyFunc('X4-728Z5-121-84gff', '-', 3)
    

    我的问题是:

    在我的导入查询中,特定字段值所需的索引取决于另一个索引的值。如果索引1的值= X4,那么我想要索引3,否则索引4。

    在单个查询中,我将此函数调用4或5次,具体取决于某些case语句的结果。

    该功能基本上是一遍又一遍地做同样的事情......但每次,我都得到一个不同的索引。如何减少工作量,使得分割字符串的艰苦工作只进行一次,在同一个查询中,我可以轻松获得不同的索引?

    请记住,这是在从外部源导入数据期间,任何建议规范化或索引视图等的答案都无济于事。

    修改

    我被要求发布我的查询:

    SELECT
        ComplexString,
        CAST(fn_MyFunc(ComplexString, '-', 1) AS NVARCHAR(2)) AS LocationCode,
        CAST(fn_MyFunc(ComplexString, '-', 2) AS NVARCHAR(25)) AS CompanyCode,
        NULLIF(CASE
            WHEN fn_MyFunc(ComplexString, '-', 1) = 'R1' THEN NULL
            ELSE CAST(fn_MyFunc(ComplexString, '-', 3) AS INT)
        END, 0) AS ManagementType,
        CASE
            WHEN fn_MyFunc(ComplexString, '-', 1) = 'R1' THEN CAST(fn_MyFunc(ComplexString, '-', 3) AS VARCHAR(25))
            ELSE CAST(fn_MyFunc(ComplexString, '-', 4) AS NVARCHAR(25))
        END AS Network,
        .
        .
        .
    FROM MyTable
    

4 个答案:

答案 0 :(得分:4)

如果所有字符串的最大值为4个部分,而不是您的函数,则可以直接执行此操作:

SELECT PARSENAME(REPLACE(column, '-', '.'), 
  CASE WHEN (condition for 4th element) THEN 1 
       WHEN (condition for 3rd element) THEN 2
       WHEN (condition for 2nd element) THEN 3
       WHEN (condition for 1st element) THEN 4
   END
FROM ...

你也可以考虑:

(a)将字符串的每个部分存储在单独的计算列中。您甚至可以对计算列进行持久化/索引。

(b)首先将字符串的各个部分分开存储 - 连接总是比分割更容易。

编辑给定更新的查询

;WITH x AS
(
  SELECT ComplexString,
         p1 = LEFT(ComplexString, 2),
         p2 = dbo.fn_MyFunc(ComplexString, '-', 2),
         p3 = dbo.fn_MyFunc(ComplexString, '-', 3),    
         p4 = dbo.fn_MyFunc(ComplexString, '-', 4)
         -- , other columns
  FROM dbo.MyTable
)
SELECT
    ComplexString,
    p1 AS LocationCode,
    LEFT(p2, 25) AS CompanyCode,
    CASE WHEN p1 <> 'RI' THEN CONVERT(INT, LEFT(p3, 3)) ELSE 0 END 
      AS ManagementType,
    LEFT(CASE WHEN p1 = 'RI' THEN p3 ELSE p4 END, 25) AS Network
FROM x;

答案 1 :(得分:4)

创建一个拆分函数,将字符串拆分为列并使用cross apply中的函数。

分割为5列的功能可能如下所示。

alter function [dbo].[SplitString]
(
    @Value nvarchar(max),
    @Delim nchar(1)
)
returns table as return
(
    select substring(T.Value, 1, T1.P - 1) as C1,
           substring(T.Value, T1.P + 1, T2.P - T1.P - 1) as C2,
           substring(T.Value, T2.P + 1, T3.P - T2.P - 1) as C3,
           substring(T.Value, T3.P + 1, T4.P - T3.P - 1) as C4,
           substring(T.Value, T4.P + 1, T5.P - T4.P - 1) as C5
    from (select @Value+replicate(@Delim, 5)) as T(Value) 
      cross apply (select charindex(@Delim, T.Value)) as T1(P)
      cross apply (select charindex(@Delim, T.Value, T1.P + 1)) as T2(P)
      cross apply (select charindex(@Delim, T.Value, T2.P + 1)) as T3(P)
      cross apply (select charindex(@Delim, T.Value, T3.P + 1)) as T4(P)
      cross apply (select charindex(@Delim, T.Value, T4.P + 1)) as T5(P)
)

它会像这样使用。

select *
from YourTable as Y
  cross apply dbo.SplitString(Y.ColumnToSplit, '-') as S

每行都会调用一次函数,你可以在字段列表中使用列C1, C2, C3, ...,或者在没有新调用的情况下使用where子句作为split函数。

答案 2 :(得分:0)

如果字符串的各个部分对您希望对数据执行的操作至关重要,则可以选择将部分存储为单个列或相关表格,从而进一步规范化。

或者,如果您需要多次访问这些部件,那么返回单个部件的表值函数可以提高性能。

答案 3 :(得分:0)

如果您的字符串具有固定(或确定的最大)数量的部分,您可以创建一组变量,如@ part1,@ part2等(如果每个部分具有特定含义,则使用更好的名称)。使用您的函数填充变量一次,并使用变量而不是重新解析。

- 或 -

使用解析方法将您的字符串转换为一个简短的临时表或表变量,其中包含一个varchar列和一个tinyint标识列(主题上SO上有很多帖子 - 和here 是一篇很好的文章,面向csv,但很容易适应你的情况)。然后,您可以使用select中的序列列来找到所需的字符串部分。