我是新手并且遇到了一个问题,我搜索了这些论坛并发现我需要使用UDF定义的函数来拆分值,因为SQL Server没有内置函数来拆分基于a的值分隔符。基于一篇文章(http://www.codeproject.com/Articles/7938/SQL-User-Defined-Function-to-Parse-a-Delimited-Str),我能够实现以下内容,但从性能角度看它并不是很好。我有大约75000条记录,需要很长时间才能运行。
最终结果是我需要获取所有购买值(由|分隔)并将每个值与名称放在一个表中。我有时可能有4个由|分隔的值或3,2无。
有人可以提供一些指示或不同的解决方案吗?也许使用像AutoIT离线的解决方案进行拆分?或者在SSIS?
我正在使用SQL Server 2012和SSIS来加载数据。
帮助!
由于
戴夫
BEGIN SET NOCOUNT ON;
DECLARE @Staging_Table TABLE
(
ACCTID INT IDENTITY(1,1),
NAME VARCHAR(50),
PURCHASES VARCHAR(255)
)
INSERT INTO @Staging_Table (Name, Purchases)
VALUES ('John','Vanilla|Chocolate|Peach')
INSERT INTO @Staging_Table (Name, Purchases)
VALUES ('Jack','Chocolate|Vanilla')
INSERT INTO @Staging_Table (Name, Purchases)
VALUES ('Mary','Peach|Vanilla|Bean')
INSERT INTO @Staging_Table (Name, Purchases)
VALUES ('Peter','Vanilla|Peach')
INSERT INTO @Staging_Table (Name, Purchases)
VALUES ('Jane','Bean|Vanilla|Chocolate|Peach')
-- Get the number of rows in the looping table
DECLARE @RowCount INT
SET @RowCount = (SELECT COUNT(ACCTID) FROM @Staging_Table)
-- Declare an iterator
DECLARE @I INT
-- Initialize the iterator
SET @I = 1
-- Loop through the rows of a table @myTable
WHILE (@I <= @RowCount)
BEGIN
-- Declare variables to hold the data which we get after looping each record
DECLARE @NAME VARCHAR(255), @PURCHASES VARCHAR(255)
-- Get the data from table and set to variables
SELECT
@NAME = NAME,
@PURCHASES = PURCHASES
FROM
@Staging_Table
WHERE
ACCTID = @I
-- Display the looped data
SELECT
@I,
@NAME,
t.txt_value
FROM dbo.fn_ParseText2Table(@PURCHASES, '|') as t
SET @I = @I + 1
END
END
答案 0 :(得分:1)
DECLARE @Tally TABLE (N INT)
DECLARE @i AS INT = 1
WHILE @i != 1000
BEGIN
INSERT INTO @Tally (N) VALUES (@i)
SET @i = @i + 1
END
--------------------------------------------------------
DECLARE @Staging_Table TABLE
(ACCTID INT IDENTITY(1, 1)
,NAME VARCHAR(50)
,PURCHASES VARCHAR(255))
INSERT INTO @Staging_Table (NAME,Purchases)
VALUES ('John','Vanilla|Chocolate|Peach')
,('Jack','Chocolate|Vanilla')
,('Mary','Peach|Vanilla|Bean')
,('Peter','Vanilla|Peach')
,('Jane','Bean|Vanilla|Chocolate|Peach')
,('Jane','Bean Vanilla Chocolate Peach')
----------------------------------------------------------
--1 variant:
SELECT E.NAME,f3.Purch
FROM @Staging_Table AS E
INNER JOIN @Tally AS T ON SUBSTRING('|' + E.Purchases, T.N, 1) = '|' AND T.N < LEN(E.Purchases) + 1
CROSS APPLY (SELECT string = SUBSTRING(' ' + E.Purchases + '|', T.N + 1, LEN(E.Purchases) + 1)) f1
CROSS APPLY (SELECT p1 = CHARINDEX('|', string)) f2
CROSS APPLY (SELECT Purch = SUBSTRING(E.Purchases, T.N, p1-1)) f3
ORDER BY E.NAME
----------------------------------------------------------
--2 variant:
SELECT E.NAME ,
( CASE WHEN CHARINDEX('|', S.string) > 0
THEN LEFT(S.string, CHARINDEX('|', S.string) - 1)
ELSE string
END ) AS Purch
FROM @Staging_Table AS E
INNER JOIN @Tally AS T ON SUBSTRING('|' + PURCHASES, T.N, 1) = '|'
AND T.N <= LEN(PURCHASES)
CROSS APPLY ( SELECT String = ( CASE WHEN T.N = 1
THEN ( CASE WHEN CHARINDEX('|',
E.PURCHASES) > 0
THEN LEFT(E.PURCHASES,
CHARINDEX('|',
E.PURCHASES) - 1)
ELSE E.PURCHASES
END )
ELSE SUBSTRING(E.PURCHASES,
T.N, 1000)
END )
) S
ORDER BY E.NAME
答案 1 :(得分:0)
存储这样的数据是不好的做法,但我会告诉你一个我发现有效的解决方案。理想情况下,您可以在SQL服务器之外执行此操作。这对我来说足够快。
CREATE TABLE #tempValues (
name VARCHAR(100),
val VARCHAR(100)
);
创建一个临时表来保存数据。解决方案是左侧的递归函数,然后调用右侧的函数。
CREATE FUNCTION breakStringUp
@nameTemp VARCHAR(100),
@stringToSplit VARCHAR(100)
BEGIN
IF FIND(@stringToSplit, '|') > 0
BEGIN
INSERT INTO #tempValues
( name, val)
VALUES
(@nameTemp, LEFT(@stringToSplit,
FIND(@stringToSplit, '|'));
breakStringUp(@nameTemp,
RIGHT( /* Recurse into the function, The index is
taken from the right side so invert it
LEN(@stringToSpit)-FIND(...) */)
)
END
ELSE
BEGIN
INSERT INTO #tempValues ..............
END
上面的代码有点复杂。我刚刚编写了伪代码,但它应该可以工作,具体取决于模式。
DECLARE CURSOR FOR ........
在行上声明一个光标并迭代。在每个名称和相应的VARCHAR上调用该函数。
SELECT * FROM #tempValues;
并清理临时表
DROP #tempValues;
答案 2 :(得分:0)
SQL Server在字符串操作方面很慢,因此任何SQL实现都会在性能方面变得非常糟糕。如果你真的速度快,你将不得不创建一个CLR函数 - 正确编写,它将击败这个特定任务的任何SQL,放下手。
另一种选择,在您的情况下可能是也可能不是可行的解决方案,是SSIS包。
与此同时,您可以尝试使用XML进行行拆分 - 但它可能比循环更糟糕(更不用说许多其他缺陷):
select t.ACCTID, t.NAME, p.c.value('./@v', 'varchar(50)') as [PurchaseItem]
from @Staging_Table t
cross apply (
select cast('<r><i v="'
+ replace(t.PURCHASES, '|', '"/><i v="')
+ '" /></r>' as xml
) as [XData]
) ca
cross apply ca.XData.nodes('/r[1]/i') p(c);