2009-05-28 35 views
58

Tôi có một bảng Persons với personaldata, v.v. Có rất nhiều cột nhưng sự quan tâm một lần ở đây là: addressindex, lastnamefirstname trong đó addressindex là địa chỉ duy nhất được khoan xuống cửa căn hộ. Vì vậy, nếu tôi có 'như dưới đây' hai người với số lastname và một trong số firstnames giống như họ có nhiều khả năng trùng lặp nhất.Kết hợp mờ bằng cách sử dụng T-SQL

Tôi cần một cách để liệt kê các mục trùng lặp này.

tabledata: 

personid  1 
firstname "Carl" 
lastname  "Anderson" 
addressindex 1 

personid  2 
firstname "Carl Peter" 
lastname  "Anderson" 
addressindex 1 

tôi biết làm thế nào thực hiện điều này nếu tôi là để phù hợp chính xác trên tất cả các cột nhưng tôi cần trận đấu mờ để làm các trick với (từ ví dụ trên) là một kết quả như:

Row  personid  addressindex  lastname  firstname 
1  2    1    Anderson  Carl Peter 
2  1    1    Anderson  Carl 
..... 

gợi ý Bất kỳ về cách giải quyết vấn đề này theo cách tốt?

+3

BTW cách nào, nó là khá khả năng nó KHÔNG phải là cùng một người trong trường hợp nhất định. Cha và con trai sống chung với nhau vào những lúc bạn biết. – HLGEM

+1

Đây luôn là vấn đề với thuật toán đánh giá địa chỉ bán thông minh. Bạn có thể giả định, nhưng bạn không bao giờ có thể chắc chắn. – Tomalak

+1

Điểm tốt mặc dù descition là một vấn đề khác dựa trên kết quả của trận đấu mờ. – Frederik

Trả lời

7

Tôi sẽ sử dụng tính năng lập chỉ mục toàn văn SQL Server, cho phép bạn thực hiện tìm kiếm và trả lại những thứ không chỉ chứa từ mà còn có lỗi chính tả.

+0

đây là một bài viết hay về nó: http://www.developer.com/db/article.php/3446891 –

+0

Thand, tôi đã xem xét nó bit se sử dụng phiên bản tiêu chuẩn và tìm kiếm văn bản đầy đủ không phải là một tùy chọn ở đây. – Frederik

+0

Tìm kiếm văn bản đầy đủ có sẵn trong tất cả các phiên bản của SQL Server 2005 và 2008 –

0

Bạn có thể sử dụng hàm SOUNDEX và hàm DIFFERENCE liên quan trong SQL Server để tìm các tên tương tự. Tham chiếu trên MSDN là here.

14

Ngoài các thông tin tốt khác ở đây, bạn có thể muốn xem xét sử dụng thuật toán ngữ âm Double Metaphone cao hơn nhiều so với SOUNDEX. Có một số Transact-SQL version (link to code here).

Điều đó sẽ hỗ trợ trong việc khớp tên với lỗi chính tả nhẹ, ví dụ: Carl so với Karl.

+0

Liên kết đó đã chết. –

+0

@ Lèsemajesté Liên kết cố định. – RedFilter

+0

Liên kết đó hiện đã chết ... lần nữa – codingbadger

1

Liên quan đến những điều bỏ xâu chuỗi, chia tách và kết hợp của bạn là lần cắt đầu tiên tuyệt vời. Nếu có các mục đã biết về dữ liệu có thể được tận dụng để giảm khối lượng công việc và/hoặc tạo ra kết quả tốt hơn, thì tốt nhất là tận dụng lợi thế của chúng. Lưu ý rằng thường để loại bỏ hoàn toàn, bạn không thể loại bỏ hoàn toàn công việc thủ công, mặc dù bạn có thể dễ dàng hơn bằng cách thu thập nhiều nhất có thể và sau đó tạo báo cáo về "trường hợp không chắc chắn của bạn".

Về đối sánh tên: SOUNDEX thật khủng khiếp về chất lượng khớp và đặc biệt xấu đối với loại công việc bạn đang cố gắng thực hiện vì nó khớp với những thứ quá xa mục tiêu. Tốt hơn là nên sử dụng kết hợp các kết quả metaphone kép và khoảng cách Levenshtein để thực hiện việc so khớp tên. Với xu hướng thích hợp, công trình này thực sự hoạt động tốt và có thể được sử dụng cho lần vượt qua thứ hai sau khi thực hiện dọn dẹp cho những người được biết của bạn.

Bạn cũng có thể muốn xem xét sử dụng gói SSIS và xem xét các biến đổi Tìm kiếm mờ và Nhóm (http://msdn.microsoft.com/en-us/library/ms345128(SQL.90).aspx).

Sử dụng tìm kiếm toàn văn bản SQL (http://msdn.microsoft.com/en-us/library/cc879300.aspx) cũng là một khả năng, nhưng có thể không phù hợp với miền vấn đề cụ thể của bạn.

4

Cá nhân tôi sử dụng thuật toán CLR của thuật toán Jaro-Winkler có vẻ hoạt động khá tốt - nó đấu tranh một chút với chuỗi dài hơn 15 ký tự và không thích địa chỉ email phù hợp. được tìm thấy here

Nếu bạn không thể sử dụng chức năng CLR cho bất cứ lý do, có thể bạn có thể thử chạy các dữ liệu thông qua một gói SSIS (sử dụng tra cứu chuyển đổi mờ) - chi tiết here

7

Kể từ phiên bản đầu tiên của Master Dịch vụ dữ liệu, bạn đã có quyền truy cập vào các thuật toán logic mờ tiên tiến hơn so với những gì S OUNDEX thực hiện. Vì vậy, miễn là bạn đã cài đặt MDS, bạn sẽ có thể tìm thấy một hàm có tên là Tương tự() trong sơ đồ mdq (cơ sở dữ liệu MDS). Thông tin

Thông tin thêm về cách thức hoạt động: http://blog.hoegaerden.be/2011/02/05/finding-similar-strings-with-fuzzy-logic-functions-built-into-mds/

17

Tôi đã phát hiện ra rằng những thứ SQL Server cung cấp cho bạn để làm khớp mờ là khá vụng về. Tôi đã thực sự may mắn với các chức năng CLR của riêng tôi bằng cách sử dụng thuật toán khoảng cách Levenshtein và một số trọng số. Sử dụng thuật toán đó, sau đó tôi đã thực hiện một UDF gọi là GetSimilarityScore lấy hai chuỗi và trả về một điểm số từ 0,0 đến 1,0. Gần hơn với 1.0 trận đấu là tốt hơn. Sau đó, truy vấn có ngưỡng> = 0,8 hoặc hơn để có được các kết quả phù hợp nhất. Một cái gì đó như thế này:

if object_id('tempdb..#similar') is not null drop table #similar 
select a.id, (
    select top 1 x.id 
    from MyTable x 
    where x.id <> a.id 
    order by dbo.GetSimilarityScore(a.MyField, x.MyField) desc 
) as MostSimilarId 
into #similar 
from MyTable a 

select *, dbo.GetSimilarityScore(a.MyField, c.MyField) 
from MyTable a 
join #similar b on a.id = b.id 
join MyTable c on b.MostSimilarId = c.id 

Chỉ cần không làm điều đó với các bảng thực sự lớn. Đó là một quá trình chậm.

Đây là CLR UDFs:

''' <summary> 
''' Compute the distance between two strings. 
''' </summary> 
''' <param name="s1">The first of the two strings.</param> 
''' <param name="s2">The second of the two strings.</param> 
''' <returns>The Levenshtein cost.</returns> 
<Microsoft.SqlServer.Server.SqlFunction()> _ 
Public Shared Function ComputeLevenstheinDistance(ByVal string1 As SqlString, ByVal string2 As SqlString) As SqlInt32 
    If string1.IsNull OrElse string2.IsNull Then Return SqlInt32.Null 
    Dim s1 As String = string1.Value 
    Dim s2 As String = string2.Value 

    Dim n As Integer = s1.Length 
    Dim m As Integer = s2.Length 
    Dim d As Integer(,) = New Integer(n, m) {} 

    ' Step 1 
    If n = 0 Then Return m 
    If m = 0 Then Return n 

    ' Step 2 
    For i As Integer = 0 To n 
     d(i, 0) = i 
    Next 

    For j As Integer = 0 To m 
     d(0, j) = j 
    Next 

    ' Step 3 
    For i As Integer = 1 To n 
     'Step 4 
     For j As Integer = 1 To m 
      ' Step 5 
      Dim cost As Integer = If((s2(j - 1) = s1(i - 1)), 0, 1) 

      ' Step 6 
      d(i, j) = Math.Min(Math.Min(d(i - 1, j) + 1, d(i, j - 1) + 1), d(i - 1, j - 1) + cost) 
     Next 
    Next 
    ' Step 7 
    Return d(n, m) 
End Function 

''' <summary> 
''' Returns a score between 0.0-1.0 indicating how closely two strings match. 1.0 is a 100% 
''' T-SQL equality match, and the score goes down from there towards 0.0 for less similar strings. 
''' </summary> 
<Microsoft.SqlServer.Server.SqlFunction()> _ 
Public Shared Function GetSimilarityScore(string1 As SqlString, string2 As SqlString) As SqlDouble 
    If string1.IsNull OrElse string2.IsNull Then Return SqlInt32.Null 

    Dim s1 As String = string1.Value.ToUpper().TrimEnd(" "c) 
    Dim s2 As String = string2.Value.ToUpper().TrimEnd(" "c) 
    If s1 = s2 Then Return 1.0F ' At this point, T-SQL would consider them the same, so I will too 

    Dim flatLevScore As Double = InternalGetSimilarityScore(s1, s2) 

    Dim letterS1 As String = GetLetterSimilarityString(s1) 
    Dim letterS2 As String = GetLetterSimilarityString(s2) 
    Dim letterScore As Double = InternalGetSimilarityScore(letterS1, letterS2) 

    'Dim wordS1 As String = GetWordSimilarityString(s1) 
    'Dim wordS2 As String = GetWordSimilarityString(s2) 
    'Dim wordScore As Double = InternalGetSimilarityScore(wordS1, wordS2) 

    If flatLevScore = 1.0F AndAlso letterScore = 1.0F Then Return 1.0F 
    If flatLevScore = 0.0F AndAlso letterScore = 0.0F Then Return 0.0F 

    ' Return weighted result 
    Return (flatLevScore * 0.2F) + (letterScore * 0.8F) 
End Function 

Private Shared Function InternalGetSimilarityScore(s1 As String, s2 As String) As Double 
    Dim dist As SqlInt32 = ComputeLevenstheinDistance(s1, s2) 
    Dim maxLen As Integer = If(s1.Length > s2.Length, s1.Length, s2.Length) 
    If maxLen = 0 Then Return 1.0F 
    Return 1.0F - Convert.ToDouble(dist.Value)/Convert.ToDouble(maxLen) 
End Function 

''' <summary> 
''' Sorts all the alpha numeric characters in the string in alphabetical order 
''' and removes everything else. 
''' </summary> 
Private Shared Function GetLetterSimilarityString(s1 As String) As String 
    Dim allChars = If(s1, "").ToUpper().ToCharArray() 
    Array.Sort(allChars) 
    Dim result As New StringBuilder() 
    For Each ch As Char In allChars 
     If Char.IsLetterOrDigit(ch) Then 
      result.Append(ch) 
     End If 
    Next 
    Return result.ToString() 
End Function 

''' <summary> 
''' Removes all non-alpha numeric characters and then sorts 
''' the words in alphabetical order. 
''' </summary> 
Private Shared Function GetWordSimilarityString(s1 As String) As String 
    Dim words As New List(Of String)() 
    Dim curWord As StringBuilder = Nothing 
    For Each ch As Char In If(s1, "").ToUpper() 
     If Char.IsLetterOrDigit(ch) Then 
      If curWord Is Nothing Then 
       curWord = New StringBuilder() 
      End If 
      curWord.Append(ch) 
     Else 
      If curWord IsNot Nothing Then 
       words.Add(curWord.ToString()) 
       curWord = Nothing 
      End If 
     End If 
    Next 
    If curWord IsNot Nothing Then 
     words.Add(curWord.ToString()) 
    End If 

    words.Sort(StringComparer.OrdinalIgnoreCase) 
    Return String.Join(" ", words.ToArray()) 
End Function 
+1

Không có quyền truy cập vào MDS và cảm ơn rằng tôi không làm việc với Big Data - điều này có vẻ rất phù hợp. Đánh giá cao các chi tiết. – justSteve

0

làm theo cách này

  create table person(
     personid int identity(1,1) primary key, 
     firstname varchar(20), 
     lastname varchar(20), 
     addressindex int, 
     sound varchar(10) 
     ) 

và sau đó tạo ra một kích hoạt

  create trigger trigoninsert for dbo.person 
     on insert 
     as 
     declare @personid int; 
     select @personid=personid from inserted; 
     update person 
     set sound=soundex(firstname) where [email protected]; 

bây giờ những gì tôi có thể làm là tôi có thể tạo ra một quy trình trông giống như thế này

  create procedure getfuzzi(@personid int) 
      as 
     declare @sound varchar(10); 
     set @sound=(select sound from person where [email protected]; 
     select personid,firstname,lastname,addressindex from person 
     where [email protected] 

này sẽ trả lại cho bạn tất cả những cái tên đó là gần như trong trận đấu với những cái tên được cung cấp bởi một PersonId đặc biệt

Các vấn đề liên quan