让我们假设一个超简化的数据示例:
ID AKey AVal
-----------------
1 AB 94
2 Q 48
3 Z 56
4 AB 12
5 T 77
... ... ...
我想分开" AB"分成几行" A"和" B"在我的导入脚本中,我通常会这样做:
INSERT INTO MyNewTable
SELECT
SRC.ID as OldIDRef,
SRC.AKey as NewKey,
SRC.AVal as NewVal
FROM OldTable as SRC
所以基本上,我想复制" AB"选择中的行并执行一些特定于此行的计算(让我们说将AVal除以2)
我能想到的唯一解决办法就是这样:
INSERT INTO MyNewTable
SELECT
SRC.ID as OldIDRef,
CASE SRC.AKey = 'BA' THEN SUBSTRING(SRC.AKey,1,1) END as NewKey,
CASE SRC.AKey = 'BA' THEN SRC.AVal / 2 END as NewVal
FROM OldTable as SRC
UNION ALL
SELECT
SRC.ID as OldIDRef,
SUBSTRING(SRC.AKey,2,1) as NewKey,
SRC.AVal / 4 + 10 as NewVal
FROM OldTable as SRC
WHERE SRC.AKey = 'BA'
我的导入中需要多次这样的过程,所以我想知道,如果我没有错过一些更简单的解决方案?
答案 0 :(得分:0)
无论如何,这是脚本,它适用于给定的值,应该是UNION ALL的两倍:
;WITH s1 as (
SELECT ID, LEFT(AKey,1) as NewKey1, AVal / 2 as NewVal1
, RIGHT(AKey,1) as NewKey2, AVal / 4 + 10 as NewVal2
, AKey, AVal
FROM Split)
SELECT ID as OldIDRef,
CASE NKey.AKey WHEN 'A' THEN NewKey1 WHEN 'B' THEN NewKey2 ELSE s1.AKey END as NewKey,
CASE NKey.AKey WHEN 'A' THEN NewVal1 WHEN 'B' THEN NewVal2 ELSE s1.AVal END as NewVal
FROM s1
INNER JOIN (SELECT 'A' as AKey UNION ALL SELECT 'B' UNION ALL SELECT NULL) as NKey
ON NKey.AKey = NewKey1 or NKey.AKey = NewKey2 or (NKey.AKey is Null and not (NewKey1 = 'A' and NewKey2 = 'B') )
答案 1 :(得分:0)
假设您可能正在寻找可以缩放到密钥长度中的N#个字符的答案,以及将新值分配给从中分割出来的密钥数量的答案。我会用递归的cte把它拉下来。使用您的示例数据添加另一行,其中包含3个字符,如'GHI',并运行此代码并查看结果比例超过2个字符。
;WITH cteRecursive AS (
SELECT
Id
,AKey
,LEFT(AKey,1) AS NewAKey
,RIGHT(Akey,LEN(AKey) - 1) AS RemainingKey
,AVal
,1 AS [Level]
FROM
@Table
UNION ALL
SELECT
t.Id
,t.AKey
,LEFT(c.RemainingKey,1) AS NewAKey
,RIGHT(RemainingKey,LEN(RemainingKey) - 1) AS RemainingKey
,t.AVal
,c.[Level] + 1 AS [Level]
FROM
@Table t
INNER JOIN cteRecursive c
ON t.Id = c.Id
AND LEN(c.RemainingKey) > 0
)
SELECT
Id
,AKey AS OriginalAKey
,NewAKey
,AVal AS OriginalAVal
,AVal / 2.00 AS NewVal
,AVal / CAST(MAX([Level]) OVER (PARTITION BY Id) AS DECIMAL(4,2)) AS NewValAsPortionOfLevel
,AVal / CAST(LEN(AKey) AS DECIMAL(4,2)) AS NewValAsPortionOfKeyLength
FROM
cteRecursive
以下是我想要的Table变量
DECLARE @Table AS TABLE (Id INT IDENTITY(1,1), AKey VARCHAR(100), AVal INT)
INSERT INTO @Table (AKey, AVal)
VALUES ('AB',94),('Q',48),('Z',56),('AB',12),('T',77),('ghi',100)
如果没有拆分密钥,你实际上可以简化递归cte并走这条路。通过使用Level < LEN(AKey)
,递归将停在正确的位置,您不需要任何其他字符串操作。
;WITH cteRecursive AS (
SELECT
Id
,AKey
,AVal
,1 AS [Level]
FROM
@Table
UNION ALL
SELECT
t.Id
,t.AKey
,t.AVal
,c.[Level] + 1 AS [Level]
FROM
@Table t
INNER JOIN cteRecursive c
ON t.Id = c.Id
AND c.[Level] < LEN(t.Akey)
)
SELECT
Id
,AKey AS OriginalAKey
,AVal AS OriginalAVal
,AVal / 2.00 AS NewVal
,AVal / CAST(MAX([Level]) OVER (PARTITION BY Id) AS DECIMAL(4,2)) AS NewValAsPortionOfLevel
,AVal / CAST(LEN(AKey) AS DECIMAL(4,2)) AS NewValAsPortionOfKeyLength
FROM
cteRecursive
另一种技术,如果你有一个非常大的数据集,并且不想使用递归,你可以构建一个Tally Table To Join。我很想知道哪个表现更好。我实际上有一个永久的计数表用于我使用的数据仓库ETL的一些记录操作,但你应该使用临时表而不是表变量。无论如何,这是方法。
DECLARE @TallyTable AS TABLE (I INT)
DECLARE @MaxLen INT
SELECT @MaxLen = MAX(LEN(AKey)) FROM @Table
IF (@MaxLen > 0)
BEGIN
WHILE @MaxLen > 0
BEGIN
INSERT INTO @TallyTable (I) VALUES (@MaxLen)
SET @MaxLen -= 1
END
END
SELECT
*
,NewValueApportionedByLengthOfKey = CAST(AVal AS DECIMAL) / ISNULL(NULLIF(LEN(AKey),0),1)
FROM
@Table t
INNER JOIN @TallyTable tt
ON LEN(t.AKey) >= tt.I
请注意,所有这些方法都假设AKey永远不会为NULL或0长度,但如果需要,所有这些方法都很容易适应处理。