我有一个 VARCHAR 列,其中包含如下数据:
abc = :abc and this = :that
我需要一个查询来查找此数据列中以冒号开头的所有特殊“单词”。我真的不需要任何其他数据(ID 或其他数据),重复就可以了。如果需要的话,我可以稍后在 Excel 中删除重复项。因此,如果这是唯一的行,我想要这样的输出:
SpecialWords
:abc
:that
我想它需要一个 CHARINDEX 或类似的东西。但由于该列中可能有多个特殊单词,我不能只找到第一个 : 并删除其余的。
非常感谢任何帮助!预先感谢!
您必须根据空格拆分此值
并仅返回以冒号开头的字段:
,我提供了两种解决方案来根据您需要的结果类型(表或单个值)实现此目的
您可以创建一个 TV 函数来将此列拆分为一个表:
CREATE FUNCTION [dbo].[GETVALUES]
(
@DelimitedString varchar(8000)
)
RETURNS @tblArray TABLE
(
ElementID int IDENTITY(1,1), -- Array index
Element varchar(1000) -- Array element contents
)
AS
BEGIN
-- Local Variable Declarations
-- ---------------------------
DECLARE @Index smallint,
@Start smallint,
@DelSize smallint
SET @DelSize = 1
-- Loop through source string and add elements to destination table array
-- ----------------------------------------------------------------------
WHILE LEN(@DelimitedString) > 0
BEGIN
SET @Index = CHARINDEX(' ', @DelimitedString)
IF @Index = 0
BEGIN
IF ((LTRIM(RTRIM(@DelimitedString))) LIKE ':%')
INSERT INTO
@tblArray
(Element)
VALUES
(LTRIM(RTRIM(@DelimitedString)))
BREAK
END
ELSE
BEGIN
IF (LTRIM(RTRIM(SUBSTRING(@DelimitedString, 1,@Index - 1)))) LIKE ':%'
INSERT INTO
@tblArray
(Element)
VALUES
(LTRIM(RTRIM(SUBSTRING(@DelimitedString, 1,@Index - 1))))
SET @Start = @Index + @DelSize
SET @DelimitedString = SUBSTRING(@DelimitedString, @Start , LEN(@DelimitedString) - @Start + 1)
END
END
RETURN
END
您可以像下面这样使用它:
DECLARE @SQLStr varchar(100)
SELECT @SQLStr = 'abc = :abc and this = :that and xyz = :asd'
SELECT
*
FROM
dbo.GETVALUES(@SQLStr)
结果:
如果您需要返回一个值(不是表格),那么您可以使用此函数,它将返回由 分隔的所有值(换行 + 回车符
CHAR(13) + CHAR(10)
)
CREATE FUNCTION dbo.GetValues2
(
@DelimitedString varchar(8000)
)
RETURNS varchar(8000)
AS
BEGIN
DECLARE @Index smallint,
@Start smallint,
@DelSize smallint,
@Result varchar(8000)
SET @DelSize = 1
SET @Result = ''
WHILE LEN(@DelimitedString) > 0
BEGIN
SET @Index = CHARINDEX(' ', @DelimitedString)
IF @Index = 0
BEGIN
if (LTRIM(RTRIM(@DelimitedString))) LIKE ':%'
SET @Result = @Result + char(13) + char(10) + (LTRIM(RTRIM(@DelimitedString)))
BREAK
END
ELSE
BEGIN
IF (LTRIM(RTRIM(SUBSTRING(@DelimitedString, 1,@Index - 1)))) LIKE ':%'
SET @Result = @Result + char(13) + char(10) + (LTRIM(RTRIM(SUBSTRING(@DelimitedString, 1,@Index - 1))))
SET @Start = @Index + @DelSize
SET @DelimitedString = SUBSTRING(@DelimitedString, @Start , LEN(@DelimitedString) - @Start + 1)
END
END
return @Result
END
GO
您可以像下面这样使用它
DECLARE @SQLStr varchar(100)
SELECT @SQLStr = 'abc = :abc and this = :that and xyz = :asd'
SELECT dbo.GetValues2(@SQLStr)
结果
表格结果换行不可见,只需将数据复制到编辑器中,它就会如图所示
参考文献
一种方法是编写专门的 SPLIT 函数。我建议从互联网上获取一个 TSQL Split 函数,看看是否可以根据您的需要调整代码。
从头开始,您可以编写一个函数,使用 CHARINDEX 循环遍历列值,直到找不到更多
:
字符。
使用
charindex
怎么样?
rextester 示例:
create table mytable (testcolumn varchar(20))
insert into mytable values ('this = :that'),('yes'), (':no'), ('abc = :abc')
select right(testcolumn, charindex(':', reverse(testcolumn)) - 1) from mytable
where testcolumn like '%:%'
参考:
更新
解决萨米人的问题:
没看到两个单词可以在一个冒号中,这样怎么样?
select replace(substring(testcolumn, charindex(':', testcolumn), len(testcolumn)), ':', '')
再次更新
我明白了,实际说法是
this = :that and that = :this
如果性能很重要,那么您需要使用内联表值函数来分割字符串并提取您需要的内容。您可以使用 delimitedSplit8K 或 delimitedSplit8K_lead 来实现此目的。
declare @string varchar(8000) = 'abc = :abc and this = :that';
select item
from dbo.DelimitedSplit8K(@string, ' ')
where item like ':%';
返回:
item
------
:abc
:that
为了获得比我上面发布的更好的性能,您可以使用 ngrams8k,如下所示:
declare @string varchar(8000) = 'abc = :abc and this = :that';
select position, item =
substring(@string, position,
isnull(nullif(charindex(' ',@string,position+1),0),8000)-position)
from dbo.ngrams8k(@string, 1)
where token = ':';
这甚至可以为您提供正在搜索的项目的位置:
position item
---------- -------
7 :abc
23 :that
我会使用递归 CTE。 https://learnsql.com/blog/recursive-cte-sql-server/
DROP TABLE IF EXISTS #allwords;
CREATE TABLE #allwords (id INT, S VARCHAR(MAX));
INSERT INTO #allwords (id, S)
VALUES
(1, 'abc = :abc and this = :that'),
(2, 'abc = :abc'),
(3, 'abc = :abc this = :that other = :other another = :another');
WITH recursivecte
AS (SELECT id,
S,
SUBSTRING( S, --Original String
CHARINDEX(':', S, 0) + 1, -- First separator
ABS(CHARINDEX(' ', -- Abs for missing space
S,
CHARINDEX(':', S, 0)) -
CHARINDEX(':', S, 0))
) AS Token,
SUBSTRING(S, CHARINDEX(':', S, 0) + 1, LEN(S)) AS TokenRemoved
FROM #allwords a
WHERE CHARINDEX(':', S, 0) > 0
UNION ALL
SELECT id,
S,
SUBSTRING(
TokenRemoved,
CHARINDEX(':', TokenRemoved, 0) + 1,
ABS(CHARINDEX(' ',
TokenRemoved,
CHARINDEX(':', TokenRemoved, 0))
- CHARINDEX(':', TokenRemoved, 0))
),
SUBSTRING(TokenRemoved,
CHARINDEX(':', TokenRemoved, 0) + 1,
LEN(TokenRemoved)) AS TokenRemoved
FROM recursivecte R
WHERE CHARINDEX(':', TokenRemoved, 0) > 0)
SELECT id,
S,
Token
FROM recursivecte
ORDER BY id,
LEN(recursivecte.TokenRemoved) DESC;