2014-09-10 58 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