我想知道我是否有像这样创建的SQL Server 2008表:
CREATE TABLE tbl (id INT PRIMARY KEY,
dvt NVARCHAR(32),
d0 TINYINT,
d1 TINYINT,
d2 TINYINT);
INSERT INTO tbl (id, dvt, d0, d1, d2)
VALUES(1, '1', NULL, NULL, NULL);
INSERT INTO tbl (id, dvt, d0, d1, d2)
VALUES(2, '', NULL, NULL, NULL);
INSERT INTO tbl (id, dvt, d0, d1, d2)
VALUES(3, '2,5', NULL, NULL, NULL);
INSERT INTO tbl (id, dvt, d0, d1, d2)
VALUES(4, '13, 34, 45, 5', NULL, NULL, NULL);
INSERT INTO tbl (id, dvt, d0, d1, d2)
VALUES(5, '1,8, 10', NULL, NULL, NULL);
我需要从'dvt'列中取出字符串并将其拆分为'd0','d1'和'd2'列。 'dvt'值可以用逗号分隔。
我可以使用C#和一个标记化函数来做到这一点,但我想知道是否可以使用SQL做同样的事情?
以前的专栏:
1, "1", NULL, NULL, NULL
2, "", NULL, NULL, NULL
3, "2,5", NULL, NULL, NULL
4, "13, 34, 45, 5", NULL, NULL, NULL
5, "1,8, 10", NULL, NULL, NULL
后面的专栏:
1, "1", 1, NULL, NULL
2, "", NULL, NULL, NULL
3, "2,5", 2, 5, NULL
4, "13, 34, 45, 5", 13, 34, 45 -- 5 is discarded
5, "1,8, 10", 1, 8, 10
答案 0 :(得分:1)
尝试这样的事情
;WITH Vals AS (
SELECT id,
dvt,
CAST('<r>'+REPLACE(dvt,',','</r><r>')+'</r>' AS XML).query('/r[1]').value('.','varchar(max)') d1,
CAST('<r>'+REPLACE(dvt,',','</r><r>')+'</r>' AS XML).query('/r[2]').value('.','varchar(max)') d2,
CAST('<r>'+REPLACE(dvt,',','</r><r>')+'</r>' AS XML).query('/r[3]').value('.','varchar(max)') d3
FROM tbl
)
SELECT id,
dvt,
CASE WHEN d1 = '' THEN NULL ELSE d1 END d1,
CASE WHEN d2 = '' THEN NULL ELSE d2 END d2,
CASE WHEN d3 = '' THEN NULL ELSE d3 END d3
FROM Vals
答案 1 :(得分:1)
此类代码的主要问题是重复使用计算。
SQL Server擅长缓存结果(如果键入完全相同的CHARINDEX()
计算5次,它只计算一次并重复使用该结果4次)< / em>的
对于那些必须输入或维护该代码的可怜程序员来说,这是一个小小的安慰。
SQL Server 2005以后的版本CROSS APPLY
确实有所帮助。重复逻辑,但结果可以重复引用,而不是重复输入计算。
SELECT
*,
SUBSTRING(dvt, 1, ISNULL(comma1.pos-1, LEN(dvt)) ) AS item1,
SUBSTRING(dvt, comma1.pos+1, ISNULL(comma2.pos-1, LEN(dvt))-comma1.pos) AS item2,
SUBSTRING(dvt, comma2.pos+1, ISNULL(comma3.pos-1, LEN(dvt))-comma2.pos) AS item3
FROM
(
SELECT 'ab,c,def,hij' AS dvt
UNION ALL
SELECT 'xyz,abc' AS dvt
)
AS data
OUTER APPLY
(SELECT NULLIF(CHARINDEX(',', data.dvt, 1 ), 0) AS pos ) AS comma1
OUTER APPLY
(SELECT NULLIF(CHARINDEX(',', data.dvt, comma1.pos+1), 0) AS pos WHERE comma1.pos > 0) AS comma2
OUTER APPLY
(SELECT NULLIF(CHARINDEX(',', data.dvt, comma2.pos+1), 0) AS pos WHERE comma2.pos > 0) AS comma3
OUTER APPLY
(SELECT NULLIF(CHARINDEX(',', data.dvt, comma3.pos+1), 0) AS pos WHERE comma3.pos > 0) AS comma4
另一种选择是简单地编写一个表值用户定义函数来执行此操作(即使函数的结果总是一行)。然后你只需CROSS APPLY
那个功能。
答案 2 :(得分:0)
这是可能的。
你可以通过对CHARINDEX
的一些重复调用并检查空值来做到这一点,但是编写一个FUNCTION
来分割字符串可能会更好更清楚。
答案 3 :(得分:-2)
我需要Sybase的String tokenizer;在名称数据中分隔1个或多个空格 姓名日期清洁,没有逗号或其他特殊字符
declare @test varchar(60)
select @test=str_replace(lower(rtrim('Jayanta Narayan Choudhuri'))," ",",")
exec sp_splitwords @test
这是基于来自http://www.sql9.com/?id=102
的Kenny Lucas的简洁提示drop proc sp_splitwords
go
create proc sp_splitwords(@instr varchar(80)) as
begin
declare @pos int,
@word varchar(80),
@list varchar(81)
create table #words(word varchar(80))
select @list = @instr + ','
set @pos = patindex('%,,%',@list)
while @pos > 0
begin
select @list = str_replace(@list,',,',',')
set @pos = patindex('%,,%',@list)
end
set @pos = patindex('%,%',@list)
while @pos > 0
begin
set @word = substring(@list, 1,@pos-1)
set @list = substring(@list, @pos+1,len(@list)-@pos)
if NOT( @word is null OR LEN(@word) = 0 )
insert into #words (word) values (@word)
set @pos = patindex('%,%',@list)
end
select * from #words
order by len(word) desc
drop table #words
end
我可以将Metaphone SQL功能移植到Sybase http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=125724
Sybase允许从一个漂亮的解决方法中递归函数 http://www.sypron.nl/quiz2008a.html#jan08
CREATE FUNCTION Metaphone2 (@str VARCHAR(100))
RETURNS VARCHAR(25) AS
BEGIN
RETURN @str
END
DROP FUNCTION Metaphone2
GO
CREATE FUNCTION Metaphone2 (@str VARCHAR(100))
RETURNS VARCHAR(25) AS
BEGIN
RETURN dbo.Metaphone(@str)
END
更改了从http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=125724
粘贴的1行功能metaphone和字符串标记符的组合意味着我可以模糊搜索名称 第一个中,姓和轮换