使用正则表达式(或类似)更新字段

时间:2012-03-04 21:22:33

标签: sql-server regex database sql-server-2008 tsql

所以我的表中有一个名为Url的字段,如下所示: /MyServer/MyPage?Param=XXX
我需要运行一个脚本才能将该字段从XXX更新为YYY。我确实有关YYY对应XXX的内容的规则,我不知道如何只更新其param为exaclty Param而不是其他的字段。< / p>

所以这些将是更新:

/MyServer/MyPage?Param=XXX        ==>  /MyServer/MyPage?Param=XXX  
/MyServer/MyOtherPage?Param=AAA   ==>  /MyServer/MyOtherPage?Param=BBB
/MyServer/MyOtherPage?Param2=JJJ  ==>  /MyServer/MyOtherPage?Param2=JJJ 
                                       (last row no changes since it is not param)

我清楚了吗?

修改:我有一个带有correpsondation XXX YYY的辅助表。请记住XXX只是一个例子。实际上参数是一个整数,因此参数值的长度是可变的

4 个答案:

答案 0 :(得分:2)

这解决了Mikael的担忧,AAAXXX等可能会出现在网址中。它还处理参数不是URL中最后一个参数的情况。

DECLARE @URLs TABLE (URL VARCHAR(2000));

INSERT @URLs SELECT '/MyServer/MyPage?Param=XXX'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param=AAA'
   UNION ALL SELECT '/MyServerAAA/MyOtherPage?Param=AAA'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param2=JJJ'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param=AAA&Param2=JJJ'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param2=AAA&Param=AAA';

DECLARE @rules TABLE(pSrc VARCHAR(32), pDest VARCHAR(32));

INSERT @rules SELECT 'XXX', 'YYY'
    UNION ALL SELECT 'AAA', 'BBB'
    UNION ALL SELECT 'JJJ', 'KKK';

;WITH src AS
(
  SELECT URL, 
    pre = LEFT(URL, CHARINDEX('?', URL)-1), 
    post =  SUBSTRING(URL, CHARINDEX('?', URL), 2000)
  FROM @URLs
), sub AS
(
    SELECT src.URL, src.pre, src.post, r.pSrc, r.pDest, 
      i = PATINDEX('%[?&]Param=' + r.pSrc + '&%', post + '&')
    FROM src INNER JOIN @Rules AS r
    ON src.post + '&' LIKE '%[?&]Param=' + r.pSrc + '&%'
)
UPDATE u SET URL = pre + STUFF(post, i+7, LEN(pSrc), pDest)
FROM @URLs AS u INNER JOIN sub ON u.URL = sub.URL;

SELECT * FROM @URLs;

结果:

URL
--------------------------------
/MyServer/MyPage?Param=YYY
/MyServer/MyOtherPage?Param=BBB
/MyServerAAA/MyOtherPage?Param=BBB
/MyServer/MyOtherPage?Param2=JJJ
/MyServer/MyOtherPage?Param=BBB&Param2=JJJ
/MyServer/MyOtherPage?Param2=AAA&Param=BBB

为后续问题添加文档链接。是的,STUFF是一个功能。

STUFF (Transact-SQL) : MSDN
PATINDEX (Transact-SQL) : MSDN

答案 1 :(得分:1)

declare @T table
(
  Col varchar(50)
)

insert into @T values
('/MyServer/MyPage?Param=XXX'),
('/MyServer/MyOtherPage?Param=AAA'),
('/MyServer/MyOtherPage?Param2=JJJ')

declare @P table
(
  ID int,
  P1 varchar(50),
  P2 varchar(50)
)

insert into @P values
(1, 'AAA', 'BBB'),
(2, 'XXX', 'YYY')

update T 
set Col = left(T.Col, len(T.Col)-len(P.P1))+P.P2
from @T as T
  inner join @P as P
    on right(T.Col, len(P.P1)+6) = 'Param='+P.P1
where P.ID = 1

select *
from @T

结果:

Col
--------------------------------------------------
/MyServer/MyPage?Param=XXX
/MyServer/MyOtherPage?Param=BBB
/MyServer/MyOtherPage?Param2=JJJ

答案 2 :(得分:0)

这是replace()方法。

编辑:增加了灵活性。

declare @myURLs table(url varchar(1000))
insert into @myURLs values ('/MyServer/MyPage?Param=XXX&Param2=JJJ'); --should update
insert into @myURLs values ('/MyServer/MyPage?Param2=XXX'); --should not update
insert into @myURLs values ('/MyServer/MyOtherPage?Param2=JJJ&Param=AAA'); --should update
insert into @myURLs values ('/MyServer/MyOtherPage?Param=AAAA'); --should not update
insert into @myURLs values ('/MyServer/MyOtherPage?Param=AAAA&Param2=JJJ'); --should not update

declare @replacements table(targetStr varchar(80), replStr varchar(80));
insert into @replacements values ('Param=AAA','Param=BBB');
insert into @replacements values ('Param=XXX','Param=YYY');

-- ANSI-92 syntax
update u
set url=REPLACE(url,r.targetStr,r.replStr)
from @myURLs u
inner join @replacements r on 
    (PATINDEX('%[?&]'+r.targetStr,url)>0
    or PATINDEX('%[?&]'+r.targetStr+'[?&]%',url) > 0);  

SELECT * 
FROM @myURLs;

结果:

url
------------------------------------------------
/MyServer/MyPage?Param=YYY&Param2=JJJ
/MyServer/MyPage?Param2=XXX
/MyServer/MyOtherPage?Param2=JJJ&Param=BBB
/MyServer/MyOtherPage?Param=AAAA
/MyServer/MyOtherPage?Param=AAAA&Param2=JJJ

答案 3 :(得分:0)

只是为了它的乐趣,并且因为原始问题确实提到了RegEx,我想我会给出一个使用正则表达式的例子。是的,这确实需要SQLCLR并不是每个人都可以或将使用它,但它是一个选项。

此示例使用名为SQL# (SQLsharp)的现有SQLCLR库,我是作者,但正则表达式函数(仅有3个例外)是免费的。

该示例使用Aaron Bertrand的答案中的示例URL和规则表变量,但将“源”字符串更改为数字(因为这是原始请求,而正则表达式在这方面是特定的)并且我添加了一个测试-case表示字符串“222”与“2222”的任何部分都不匹配。

DECLARE @URLs TABLE (URL VARCHAR(2000));

INSERT @URLs SELECT '/MyServer/MyPage?Param=111'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param=222'
   UNION ALL SELECT '/MyServer222/MyOtherPage?Param=222'
   UNION ALL SELECT '/MyServer222/MyOtherPage?Param=2222'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param2=333'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param=222&Param2=333'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param=2222&Param2=333'
   UNION ALL SELECT '/MyServer/MyOtherPage?Param2=222&Param=222';

DECLARE @Rules TABLE(pSrc VARCHAR(32), pDest VARCHAR(32));

INSERT @Rules SELECT '111', 'YYY'
    UNION ALL SELECT '222', 'BBB'
    UNION ALL SELECT '2222', 'bbb'
    UNION ALL SELECT '333', 'KKK';


SELECT SQL#.RegEx_Replace(u.url,
            '(.*Param=)' + r.pSrc + '([^0-9]+|$)',
            '$1' + r.pDest + '$2',
            -1, 1, '')
FROM @URLs u
CROSS JOIN @Rules r
WHERE SQL#.RegEx_IsMatch(u.url,
            '(.*Param=)' + r.pSrc + '([^0-9]+|$)',
            1, '') = 1

-- OR, using RegEx look-behind and look-ahead
SELECT SQL#.RegEx_Replace(u.url,
            '(?<=Param=)' + r.pSrc + '(?=[^0-9]+|$)',
            r.pDest,
            -1, 1, '')
FROM @URLs u
CROSS JOIN @Rules r
WHERE SQL#.RegEx_IsMatch(u.url,
            '(?<=Param=)' + r.pSrc + '(?=[^0-9]+|$)',
            1, '') = 1

结果:

/MyServer/MyPage?Param=YYY
/MyServer/MyOtherPage?Param=BBB
/MyServer222/MyOtherPage?Param=BBB
/MyServer222/MyOtherPage?Param=bbb
/MyServer/MyOtherPage?Param=BBB&Param2=333
/MyServer/MyOtherPage?Param=bbb&Param2=333
/MyServer/MyOtherPage?Param2=222&Param=BBB

为了公平对待严格的T-SQL解决方案,这不是针对性能而是针对功能性和灵活性进行测试的。哪个解决方案性能更好,必须在您的服务器上进行测试。