在insert trigger中将范围值解析为逗号分隔的值数组

时间:2013-03-25 12:31:39

标签: sql sql-server sql-server-2008 parsing triggers

我需要一些帮助在SQL2008服务器上插入触发器之前解析值。

我有一个包含文本字段的表(让我们称之为源代码)。 字段值可能如下所示

  

10-15,20-22,25-26,

我希望在另一个字段中使用逗号分隔值(比如说目标):

  

10,11,12,13,14,15,20,21,22,25,26,

这可以在插入触发器之前完成,还是需要某种外部应用程序?

谢谢。

2 个答案:

答案 0 :(得分:1)

首先,您需要创建Table Valued function,其startend值生成sequence。这是使用recursive cte

完成的
CREATE FUNCTION FnGetRange(@startValue int,@endValue int)
RETURNS  @rtnTable TABLE 
(
  generatedVal VARCHAR(MAX)
)
AS
BEGIN
;with cte(startValue,rangeVal,generatedVal)
as
(
Select @startValue,@endValue,@startValue as generatedVal
union all
Select  startValue, rangeVal, generatedVal+1
from cte r
where  rangeVal > generatedVal 
)
Insert into @rtnTable
Select  generatedVal from cte
return
END

您需要splitsingle column导入rows,以便获取范围并将其传递给function

 ;with cte(range) as 
 (
   SELECT 
   RIGHT(LEFT(T.rangeVal,Number-1),
  CHARINDEX(',',REVERSE(LEFT(','+T.rangeVal,Number-1)))) as range
  FROM
  master..spt_values,
  yourTable T
  WHERE
  Type = 'P' AND Number BETWEEN 1 AND LEN(T.rangeVal)+1
  AND
 (SUBSTRING(T.rangeVal,Number,1) = ',' OR SUBSTRING(T.rangeVal,Number,1)  = '') 
)

以上解决方案已发布here,它基本上使用master..spt_values来生成序列

cte将返回结果

range
10-15
20-22
25-26

现在您需要split范围StartValueEndValue

rangeCte  (startValue,endValue) as
(
Select parsename(replace(range,'-','.'),2) as startValue,
parsename(replace(range,'-','.'),1) as endValue
from cte
)

以上rangeCTE将返回

等数据
  startValue  endValue
    10         15
    20         22
    25         26

获得这些值后,您只需使用FnGetRange

将其传递给函数cross apply
 RowValue (rangeSep) as 
( Select val.generatedVal as rangeSep from rangeCte r
  CROSS APPLY
  dbo.FnGetRange(r.StartValue,r.endValue) AS val
) 

这将生成序列但它将分为多行。要将其转换为单row,请使用xml path

 SELECT STUFF(
(SELECT ',' + rangeSep
   FROM RowValue
    FOR XML PATH(''),type).value('.','varchar(max)'),1,1,'') 

现在将最终查询的所有CTE's组合在一起

;with cte(range) as 
(
  SELECT 
  RIGHT(LEFT(T.rangeVal,Number-1),
  CHARINDEX(',',REVERSE(LEFT(','+T.rangeVal,Number-1)))) as range
  FROM
  master..spt_values,
  yourTable T
  WHERE
  Type = 'P' AND Number BETWEEN 1 AND LEN(T.rangeVal)+1
  AND
 (SUBSTRING(T.rangeVal,Number,1) = ',' OR SUBSTRING(T.rangeVal,Number,1)  = '') 
),rangeCte  (startValue,endValue) as
  (
Select parsename(replace(range,'-','.'),2) as startValue,
parsename(replace(range,'-','.'),1) as endValue
from cte
  ),RowValue (rangeSep) as 
    ( Select val.generatedVal as rangeSep from rangeCte r
      CROSS APPLY
      dbo.FnGetRange(r.StartValue,r.endValue) AS val
    ) 
     SELECT STUFF(
    (SELECT ',' + rangeSep
     FROM RowValue
     FOR XML PATH(''),type).value('.','varchar(max)'),1,1,'') 

结果将是

 10,11,12,13,14,15,20,21,22,25,26

正如其他人所建议的那样,您应该认真更改table design。而不是将其存储为string创建columns来存储range类型int }

更新了

只是在同一页面上。您在源表上创建Insert Trigger,其中包含10-15,20-22,25-26之类的值。您需要将这些值转换为sequence并将其插入Target表。如果是这种情况,则可以使用以下代码。

基本上创建的触发器Derived Table插入insertedtrigger中逻辑表的数据。然后使用上面的nested CTE's插入sequencetarget

create trigger tri_inserts on a
after insert
as
set nocount on
Declare @RangeTable table
(rangeVal varchar(max))

Insert into @RangeTable
Select rangeColumn from INSERTED

;with cte(range) as 
(
 SELECT 
 RIGHT(LEFT(T.rangeVal,Number-1),
 CHARINDEX(',',REVERSE(LEFT(','+T.rangeVal,Number-1)))) as range
 FROM
 master..spt_values,
 @RangeTable T
 WHERE
 Type = 'P' AND Number BETWEEN 1 AND LEN(T.rangeVal)+1
 AND
 (SUBSTRING(T.rangeVal,Number,1) = ',' OR SUBSTRING(T.rangeVal,Number,1)  = '') 
 ),rangeCte  (startValue,endValue) as
   (
    Select parsename(replace(range,'-','.'),2) as startValue,
    parsename(replace(range,'-','.'),1) as endValue
    from cte
   ),RowValue (rangeSep) as 
    ( Select val.generatedVal as rangeSep from rangeCte r
      CROSS APPLY
      dbo.FnGetRange(r.StartValue,r.endValue) AS val
    ) 
     Insert into Target(DestColumn)     --Change the target name 
     SELECT STUFF(
     (SELECT ',' + rangeSep
      FROM RowValue
      FOR XML PATH(''),type).value('.','varchar(max)'),1,1,'') 
  GO

答案 1 :(得分:0)

我建议这些数字应该存储在一个单独的表中,例如范围(start int,end int)与你引用的表的多个-1链接应该使这样的查询更加简单并且为你节省其他潜在的麻烦,但也许你有充分的理由这样做。在这种情况下,我建议创建一个UDF来生成CSV字符串,并将该字段声明为引用UDF的计算列。如果这是一个频繁使用或经常更新的表,请注意潜在的性能问题。