Sql server 如何重写查询,使函数不会被多次调用?

Sql server 如何重写查询,使函数不会被多次调用?,sql-server,tsql,Sql Server,Tsql,我每月定期导入大量数据。在转换过程中,我将一个字符串拆分为多个列,但这不仅仅是一个简单的拆分。有一点逻辑决定了字符串的哪个部分进入哪个字段 我编写了一个内联函数,它将字符串分成多个部分,并在指定的索引处为您提供值 参数包括: 字符串值 分隔符 指数 例如: 如果字符串值为X4-728Z5-121-84gff,并且您希望函数为您提供121,那么您可以如下调用函数: fn_MyFunc('X4-728Z5-121-84gff', '-', 3) 我的问题是: 在导入查询中,特定字段值所需的索引取决

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

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

参数包括:

字符串值 分隔符 指数 例如:

如果字符串值为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个部分,而不是函数,您可以直接内联执行此操作:

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 ...
你也可以考虑:

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

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;

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

-或-

使用解析方法将字符串转换为一个狭窄的临时表或表变量,其中包含一个varchar列和一个tinyint identity列。主题和
是一篇很好的文章,面向csv,但很容易适应您的情况。然后,您可以使用select中的sequence列来定位所需的字符串部分。

创建拆分函数将字符串拆分为列,并在交叉应用中使用该函数

拆分为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
它会像这样使用

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函数。

查找索引/物化视图。这假设对于同一行,索引不会频繁更改。或者,您可以使用基于函数的索引。出于兴趣,为什么要多次调用该函数?我知道这可能是出于性能原因,但你说过这不是一个实时操作,因为它每月都会发生。穆特斯:你是对的,但是导入是在工作时间由用户按需运行的,而且数据相当大。我想减少对其他用户的影响。@bum:question已编辑为包含我的querytank you。我对这项技术很熟悉。我的琴弦有四个以上的部分。至于您的其他想法,这是在导入大量数据期间,目标是在数据转换期间分别存储每个部分。谢谢您的回答。我对我的问题进行了编辑,以反映这是在从外部源导入数据的过程中发生的,因此,规范化没有帮助。您的答案假设字符串只存在一次。在我的例子中,每一行上都存在一个不同的字符串。我无法为每行设置变量值并将其内联使用。谢谢。唯一的问题是零件的数量是静态的。如果我有一个值,在本例中,其零件号小于5,则零件号是可变的-因此case语句,这将引发一个错误。@GabrielMcAdams,可以处理。更新答案。但是,您必须事先确定您感兴趣的最大列数。如果必须选择+1。我将选择此解决方案。除了只运行一次之外,查询优化器还可以按以下方式进行优化: -因为它是一个内联表值函数。