2016-09-29 4 views
0

Ich versuche eine SQL-UDF oder -Anweisung zu erstellen, um ein freies Textfeld zu analysieren und den Ländernamen zu finden, aber ich kann das nicht erfolgreich durchführen.SQL-Skript zum Extrahieren eines Landes aus dem Freitextfeld

Um Ihnen den vollen Kontext zu geben, habe ich eine Transaktionstabelle (tbltransactions unten), die Transaktionsdetails enthält und eines der Felder ist dieses freie Textfeld. Dies sollte idealerweise enthalten Name des Zahlungsempfängers, Empfängeradresse und Empfängerland (in dieser Reihenfolge). Aber wie Sie es von einem freien Textfeld erwarten, gibt es alle möglichen Kombinationen. Dies bedeutet auch, dass der Name eines Landes falsch geschrieben, abgekürzt, verkürzt oder komplett fehlen könnte. Glücklicherweise haben die meisten Transaktionen Länder am Ende des Textblocks angegeben! Es gibt ein weiteres Feld in der Tabelle, in dem der Benutzer einen dreistelligen Ländercode eingibt (obligatorisch). Dies kann oder kann nicht mit dem entsprechen, was er in das Freitextfeld eingegeben hat. Im Folgenden finden Sie die Dummy-Daten in der Tabelle:

TransID  ISOCode BeneAddress 
------------------- ----------- 
20   IRN  aaaa bb cccc Islamic Rupublic of Iran 
19   IRN  aaaa bb cccc Iran, Islamic Republic of 

Jetzt habe ich eine Tabelle nachschlagen gemacht (tblCountryMappings), die eine Liste aller Länder und möglichen Variationen ihrer Namen speichert (und die meisten von ihnen!).

Eg. 'Mazedonien, Republik', 'MAZEDONIEN, DIE EHEMALIGE JUGOSLAWISCHE REPUBLIK VON', 'MAZEDONIEN', 'MASEDONIA' usw.

Im Folgenden finden Sie Dummy-Daten für diese Tabelle:

ID ISONumericCode countryName     matchIdentifier   matchIdentifierType 
---------------------------------------------------------------------------------------------- 
209 364   Iran, Islamic Republic of IR       ISOAlphaCode_2 
210 364   Iran, Islamic Republic of IRN      ISOAlphaCode_3 
495 364   Iran, Islamic Republic of Iran      Short_Name 
1163 364   Iran, Islamic Republic of Iran, Islamic Republic of Original_Name 
1309 364   Iran, Islamic Republic of Islamic Rupublic of Iran Alternate_Name 

Wie Sie kann sehen, es gibt eine Eins-zu-viele-Zuordnung zwischen den Tabellen. Das Ziel Ziel ist in der Lage, die Transaktion zu analysieren und herauszufinden, für welches Land es bestimmt war (in erster Linie basierend auf dem Freitextfeld, nicht nur der ISO-Code). Zum Beispiel sollten wir in der Lage sein zu sehen, dass Transaktion 123 "Irak" im ISO-Code und "Iran" im Freitext hat, und die Freitext-Übereinstimmung war auf dem ISO 3-Zeichen "IRN". Ich muss auch sicherstellen, dass das Matching in Grenzsituationen (z. B. Zeilenende, umgeben von Anführungszeichen) funktioniert, aber nicht, wenn es in der Mitte eines Textblocks steht (z. B. nicht mit dem Saudi-Arabien 2-stelligen Code "SA") jeder nannte "Samuel").

Ich habe dieses grundlegende Skript geschrieben, um das letzte Wort aus Freitext zu extrahieren, und dies könnte dann verwendet werden, um matchIdentifier in tblCountryMappings beizutreten, aber es ist offensichtlich ein extrem schlechter Versuch.

Wenn Sie mir bitte helfen könnten, diese Lösung zu bauen, wird es wirklich geschätzt. Bitte entschuldigen Sie mich, wenn ich irgendwelche Regeln des Postings verletzt habe, da dies das erste Mal ist. Fühlen Sie sich frei, nach weiteren Informationen zu fragen, und ich werde es frühestens veröffentlichen.

Vielen Dank im Voraus.

Prost

Antwort

0

Ich bezweifle, dass es eine perfekte Lösung ist da draußen, da ich seltsame Szenarien vorstellen kann, wo einzelne Straßennamen oder Staatsnamen können auf bestimmte Ländernamen ähnlich sein. Nachdem Sie gesagt haben, dass Sie Ihre Lookup-Tabelle mit der Transaktionstabelle verknüpfen können, verwenden Sie die LIKE-Anweisung. Auf diese Weise können Sie reguläre Ausdrücke verwenden, um das Land in der Adresse abzugleichen. Der Name des Landes ist entweder am Ende oder am Anfang begrenzt oder endet entweder mit einem ',' oder Leerzeichen. Es sollte Ihre Abfrage etwas vereinfachen, aber wie ich bereits erwähnte, wird es nicht perfekt sein.

Das folgende Beispiel zeigt, wie die Abfrage aussehen wird.

DECLARE @tbltransactions TABLE 
(
    TransID  INT 
    ,ISOCode  NVARCHAR(3) 
    ,BeneAddress NVARCHAR(100) 
) 

DECLARE @tblCountryMappings TABLE 
(
    ID     INT IDENTITY 
    ,CountryName  NVARCHAR(100) 
    ,MatchIdentifier NVARCHAR(100) 
) 

INSERT INTO @tbltransactions 
(
    TransID  
    ,ISOCode  
    ,BeneAddress 
) 
VALUES 
(1   ,'IRN'  ,'aaaa bb cccc Islamic Rupublic of Iran') , 
(2   ,'IRN'  ,'aaaa bb cccc "Iran", Islamic Republic of'), 
(3   ,'IRN'  ,'aaRSAbb cccc IRN'), 
(4   ,'IRN'  ,'aaaa bb cccc IR'), 
(5   ,'IRN'  ,'aaaa bb cccc The Country of Fred') 


INSERT INTO @tblCountryMappings 
(  
    CountryName  
    ,MatchIdentifier 
) 
VALUES 
('Iran, Islamic Republic of', 'IR'),   
('Iran, Islamic Republic of', 'IRN'),   
('Iran, Islamic Republic of', 'Iran'), 
('South Africa, Republic of', 'RSA'), 
('South Africa, Republic of', 'R.S.A.'), 
('South Africa, Republic of', 'South Africa') 


SELECT  T.TransID 
      ,T.BeneAddress 
      ,ISNULL(M.CountryName, '< Could not match country>') AS CountryName 
      ,M.MatchIdentifier 
FROM  @tbltransactions T 
LEFT OUTER JOIN @tblCountryMappings M ON 
       ( 
         (T.BeneAddress LIKE '%[, "]' + M.MatchIdentifier + '[, "]%') -- Match any address which contains a word that start with a comma or space or quote ([, "]) and is then followed by the MatchIdentifier and then end with either a comma or space or quote. 
           OR 
         (T.BeneAddress LIKE '%[, "]' + M.MatchIdentifier) -- Match any address which contains a word that start with a comma or space or quote ([, "]) and is then ends with the MatchIdentifier. 
           OR 
         (T.BeneAddress LIKE M.MatchIdentifier + '[, "]%') -- Match any address which contains a word that start with the MatchIdentifier and then end with either a comma or space or quote. 
           OR 
         (T.BeneAddress LIKE M.MatchIdentifier) -- Match the address with an exact match of the MatchIdentifier 
       ) 

Im obigen Beispiel SQL die BeneAddress mit regulärem Ausdruck auf dem Wert des Feldes MatchIdentifier erzeugt basiert.

Beispiel in den TblCountryMappings das MatchIdentifier Feld wird die folgenden Werte für Iran haben.

  • IR
  • IRN
  • Iran

Dadurch werden die folgenden regulären Ausdrücke erzeugen:

  • % [ "] IR ["]% - Match beliebige Zeichenfolge Das Wort enthält ein Wort, das mit einem Komma oder Leerzeichen oder einem Anführungszeichen ([, "]) beginnt, gefolgt von IR und endet dann mit einem Komma oder Leerzeichen oder einem Anführungszeichen.
  • % [, "] IRN [,"]% - Entspricht einer beliebigen Zeichenfolge, die ein Wort mit einem Komma oder Leerzeichen oder einem Anführungszeichen ([, "]) enthält, gefolgt von IRN und endet mit einem Komma oder Platz oder Zitat.
  • % [, "] Iran [,"]% - Entspricht einer Zeichenfolge, die ein Wort enthält, das mit einem Komma oder Leerzeichen oder einem Anführungszeichen ([, "]) beginnt, gefolgt von Iran und dann mit einem Komma endet oder Raum oder Zitat.

die Möglichkeit passen, dass das Land am Ende der Zeichenfolge sein könnte wir einen zusätzlichen OR Zustand umfassen, in denen kein Muster Spiel für das Ende definiert ist. ähnlich, dass das Land die Möglichkeit entsprechen Name könnte am Anfang der Zeichenfolge sein wir eine zusätzliche OR Bedingung enthalten, wo keine Musterübereinstimmung für den Start definiert ist

+0

Dank für diese Lösung @Edmond! Funktioniert ziemlich gut. Könnten Sie mir bitte sagen, wie Sie beigetreten sind? (T.BeneAddress LIKE '% [,]' + M.MatchIdentifier + '[,]%') '. Meine SQL ist ein bisschen schwach, so konnte nicht wie Joins arbeiten, ohne die übliche A.x = B.x. Auch dann, ich cud versuchen, mehr Bedingungen in der Regex zu berücksichtigen Szenarien wie countryname in Anführungszeichen oder am Anfang der Zeichenfolge oder wenn Freetext hat nur den Namen des Landes nichts anderes - coz zur Zeit geben sie keine Übereinstimmung zurück. Ein weiteres Problem, das mir auffiel, ist, dass es auch ein Spiel gegen die Namen von Zahlungsempfängern gibt, zB "Mr. bb Sudan" wird Barbados und Sudan beide zurückgeben. Danke! –

+0

@ V. Asher der Join selbst ist wirklich nicht viel anders als eine traditionelle Join, außer statt eine genaue Übereinstimmung zwischen T.BeneAddres und M. MatchIdentifier wird es stattdessen die T.BeneAddres beitreten, wenn es mit dem Muster wie durch den regulären übereinstimmt Ausdruck. Ich werde die Antwort mit einem Beispiel zum Umgang mit Anführungszeichen und Ländernamen am Anfang des Strings mit einigen zusätzlichen Erklärungen aktualisieren. Leider kann man bei Szenarien, in denen der Name der Person auch mit einem Kreisnamen übereinstimmen kann, nicht viel tun, da der Name des Landes am Anfang der Zeichenkette oder am Ende steht. –

+0

Vielen Dank! Also, wenn ich dich richtig verstanden habe: '(T.BeneAddress LIKE '% [,]' + M.MatchIdentifier + '[,]%')' wird nur beitreten, wenn beide Felder ** ',' ** und '(T .BeneAddress LIKE '% [,]' + M.MatchIdentifier) ​​'wird nur beitreten, wenn T.BeneAddress es hat und M.MatchIdentifier nicht? Für das letzte von Ihnen erwähnte Bit, für ~ 90% der Datensätze, ist der Name des Landes am Ende. Prost! –

0

Wenn ich Ihre Frage verstehe.

Das Folgende liefert die HÖCHSTE ANZAHL DER MATCH HITS.Es macht einen Parsing-Funktion benötigen unten aufgeführt:

erstellen Beispieldaten

Declare @YouTable table (TransID int,ISOCode varchar(50),BeneAddress varchar(500)) 
Insert Into @YouTable values 
(20,'IRN','aaaa bb cccc Islamic Rupublic of Iran'), 
(19,'IRN','aaaa bb cccc Iran, Islamic Republic of') 

Declare @ISO table (ID int,ISONumericCode int,countryName varchar(50),matchIdentifier varchar(50),matchIdentifierType varchar(50)) 
Insert Into @ISO values 
(209 ,364,'Iran, Islamic Republic of','IR',      'ISOAlphaCode_2'), 
(210 ,364,'Iran, Islamic Republic of','IRN',      'ISOAlphaCode_3'), 
(495 ,364,'Iran, Islamic Republic of','Iran',      'Short_Name'), 
(1163 ,364,'Iran, Islamic Republic of','Iran, Islamic Republic of','Original_Name'), 
(1309 ,364,'Iran, Islamic Republic of','Islamic Rupublic of Iran' ,'Alternate_Name') 

Actual SQL

;with cteBase as (
     Select A.*,B.*,C.* 
      From @YouTable A 
      Cross Apply (Select * from [dbo].[udf-Str-Parse](A.BeneAddress,' ') ) B 
      Cross Apply (Select * from @ISO where matchIdentifier like '%'+B.RetVal+'%') C),  
     cteSumm as (
     Select TransID,ID,RowNr=Row_Number() over (Partition By TransID Order by Count(*) Desc) 
     From cteBase 
     Group By TransID,ID 
) 
Select B.*,C.* 
From cteSumm A 
Join @YouTable B on (A.RowNr=1 and A.TransID = B.TransID) 
Join @ISO C  on (A.RowNr=1 and A.ID=C.ID) 

Returns

enter image description here

Die UDF

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10)) 
Returns Table 
As 
Return ( 
    Select RetSeq = Row_Number() over (Order By (Select null)) 
      ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
    From (Select x = Cast('<x>'+ Replace(@String,@Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i) 
); 
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',') 
--Select * from [dbo].[udf-Str-Parse]('John||Cappelletti||was||here','||') 
+0

Hallo John, danke für die Lösung .. es funktioniert ziemlich gut.Das einzige Problem mit diesem ist, dass es versucht, auf jedes Wort in der BeneAddress wie "von", "das" usw. zB zu passen. "Mr. X Volksrepublik China" wird aufgrund des passenden Wortes "von" eine Übereinstimmung mit "Iran, Islamische Republik" finden. –

+0

@ V.Asher Ja, aber es ist der Top-Hit. Ich werde es ein bisschen mehr nudeln. –

+0

Prost @ John. Es ist vielleicht nicht machbar, aber könnte es eine Möglichkeit geben, so etwas wie eine prozentuale Übereinstimmung zu haben, dh wenn der Freitext "Iran, Islamische Republik" mit der Liste der Länder übereinstimmt, hat er einen höheren Übereinstimmungsgrad mit "Iran" dann mit "Iran, Islamische Republik" als mit "Volksrepublik China", wenn das Sinn macht? –

Verwandte Themen