2014-09-10 57 views
0

我打算写两个输入字符串的TSQL功能和作为输出的话相似的百分比,例如类似的话两个字符串之间的数字:计算SQL

SELECT [dbo].[FN_CalcSimilarWords]('Golden horses hotel','Hotel Golden Horses') 

返回:

3/3 

SELECT [dbo].[FN_CalcSimilarWords]('Golden horses','Golden horses Malaysia') 

返回:

2/3 

我在考虑循环和比较分析字符串后的单词到This split function,任何其他想法,以获得更好的性能?

+0

SQL在字符串操作上并不是很强大,如果您允许在服务器上安装一个,您可能需要考虑CLR例程。 – Sparky 2014-09-10 10:06:45

+0

@Sparky,是的,我可以安装它,任何想法或有用的链接来实施解决方案? – Alaa 2014-09-10 10:09:40

+0

在这里有关于堆栈溢出的链接,它实际上很容易在C#中,这里是一个这样的链接http://stackoverflow.com/questions/10648141/get-different-and-common-items-in-two-arrays- with-linq – Sparky 2014-09-10 10:20:36

回答

1

如果你想在SQL中做到这一点,我会采取一种方法。

创建两个临时表,使用分流常规,叫Words1和Words2

现在连接表,并获得数,即

select count(*) 
from Words1 w1 
join Words2 w2 on w1.word=w2.word 

让SQL做它是为

优化方法

这里是如何从两个表中获取计数

select count(distinct w1.word) as Matches, 
     count(distinct w1.word) as FromW1, 
     count(distinct w2.word) as FromW2 
    from #Words1 w1 
    left join #Words2 w2 on w1.word=w2.word 
+0

感谢您的回答,这只会返回类似单词的计数,还有什么方法可以在不重新调用Split函数的情况下返回Words1和Words2的计数? – Alaa 2014-09-10 10:55:05

+1

感谢您更新答案,但它返回不正确的结果,匹配和FromW1始终相等。 – Alaa 2014-09-10 11:28:54

+1

匹配总是相等的,但是代码假设#words1大于单词2.我将很快调整代码以返回两个表中较大的一个计数 – Sparky 2014-09-10 12:25:25

0

如果您在部署CLR程序集时没有任何限制,则可以尝试此路线并比较性能。

0

如果您不担心具有普通项目的确切数量,则可以使用SQL Server全文搜索功能。 ContainsTableFREETEXT函数都返回一个Rank。看到这里的细节

Full Text Ranking

1

原来的答案: SQL Fiddle

我看到@ T-克劳森在PTR Blog

编辑

这种技术修正,以解决意见问题。DK:

SQL Fiddle

MS SQL Server 2012的架构设置

CREATE TABLE StringTable 
(
    Id INT IDentity, 
    String varchar(max) 
) 

INSERT INTO StringTable 
VALUES ('xx xx Golden horses Malaysia'), 
     ('xx xx xx xx xx') 

查询1

WITH StringsCTE 
AS 
(
    SELECT ID,String As StringValue, 
      CASE CHARINDEX(' ', String) 
       WHEN 0 THEN String 
       ELSE LEFT(String, CHARINDEX(' ',String) -1) 
      END AS Word, 
      1 as Position, 
      CASE CHARINDEX(' ',String) 
       WHEN 0 THEN '' 
       ELSE RIGHT(String, LEN(String) - CHARINDEX(' ',String)) 
      END AS RestOfLine 
    FROM StringTable 
    UNION ALL 

    SELECT Id,S.StringValue, 
      CASE CHARINDEX(' ',RestOfLine) 
       WHEN 0 THEN RestOfLine 
       ELSE LEFT(RestOfLine, CHARINDEX(' ',RestOfLine) -1) 
      END, 
      Position + 1, 
      CASE CHARINDEX(' ',RestOfLine) 
       WHEN 0 THEN '' 
       ELSE RIGHT(RestOfLine, LEN(RestOfLine) - CHARINDEX(' ',RestOfLine)) 
      END 
    FROM StringsCTE S 
    WHERE s.RestOfLine != '' 
), 
WordsPerString 
As 
(
    SELECT S.Id, COUNT(s.Word) As NumberOfWords 
    FROM StringsCTE S 
    GROUP BY S.Id 
) 
SELECT COUNT(*) As Matches, (SELECT MAX(NumberOfWords) FROM WordsPerString) as Total 
FROM StringsCTE S1 
INNER JOIN StringsCTE S2 
    ON S1.Word = S2.Word AND S1.Id <> S2.Id 
WHERE S1.Id = 1 AND 
    NOT EXISTS -- Not already matched 
    (SELECT * FROM StringsCTE S3 WHERE S3.Word = S2.Word AND S3.Id <> S1.ID AND S3.Position < S2.Position) 

Results

| MATCHES | TOTAL | 
|---------|-------| 
|  2 |  5 | 
+1

在stringtable中试试这两个值:'xx xx Golden horses Malaysia','xx xx xx xx xx',它将返回5列10个匹配项。所以它有200%的匹配 – 2014-09-10 12:17:10

+0

@ t-clausen.dk更新了解决你的问题的答案。 – 2014-09-10 16:07:16

1

有了这个解决方案,我假设你想重复删除。切换第一个和第二个参数对结果没有影响。

它返回一个值,而不是百分比,因为函数只能返回1个值或一个表。我假设你想在0和1之间的值,使得2/3 = 0.67或67%,如果你用100

CREATE function f_functionx 
(
    @str1 varchar(2000), 
    @str2 varchar(2000) 
) 
returns decimal(5,2) 
as 
BEGIN 
DECLARE @returnvalue decimal(5,2) 
DECLARE @list1 table(value varchar(50)) 
INSERT @list1 
SELECT t.c.value('.', 'VARCHAR(2000)') 
FROM (
    SELECT x = CAST('<t>' + 
     REPLACE(@str1, ' ', '</t><t>') + '</t>' AS XML) 
) a 
CROSS APPLY x.nodes('/t') t(c) 

DECLARE @list2 table(value varchar(50)) 
INSERT @list2 
SELECT t.c.value('.', 'VARCHAR(2000)') 
FROM (
    SELECT x = CAST('<t>' + 
     REPLACE(@str2, ' ', '</t><t>') + '</t>' AS XML) 
) a 
CROSS APPLY x.nodes('/t') t(c) 


;WITH isect as 
(
    SELECT count(*) match FROM 
    (
    SELECT value FROM @list1 
    INTERSECT 
    SELECT value FROM @list2 
) x 
), total as 
(
    SELECT max(cnt) cnt 
    FROM 
    (
    SELECT count(distinct value) cnt FROM @list1 
    UNION ALL 
    SELECT count(distinct value) FROM @list2 
) x 
) 
SELECT 
    @returnvalue = cast(isect.match as decimal(9,2))/total.cnt 
FROM total 
CROSS JOIN isect 

RETURN @returnvalue 
END 

GO 

乘你这样调用该函数:

SELECT dbo.f_functionx('Golden horses', 'Golden horses') 
SELECT dbo.f_functionx('Golden horses', 'Golden horses XX') 

返回:

1 
0.67 
+0

这似乎是一个天才答案,不幸的是我无法比较性能,因为大部分记录都包含'&'。 SQL正在抛出: 'XML解析:...非法名字字符',我相信我必须用'&' – Alaa 2014-09-10 13:54:37