Выберите похожие строки в одной таблице - PullRequest
2 голосов
/ 11 марта 2009

Я пытаюсь написать запрос, чтобы найти всех пользователей USERS в таблице USERS, которые похожи. Это все равно, что найти все непонятные строки, но использовать оператор LIKE, а не оператор equals. Ниже приведен пример столбца таблицы USERS

 USERNAME 
------------
tim.smith
doug.funny
tim.smith1
dan.snyder
tim.smith20
doug.funny2
emily.hunt

после запроса вывод должен выглядеть следующим образом.

tim.smith
tim.smith1
tim.smith20
doug.funny
doug.funny2

Ответы [ 7 ]

1 голос
/ 11 марта 2009

Я искал законную причину использования перекрестного соединения

Select Distinct u1.UserID 
from username  u1 Cross join username u2
where u1.UserID <> u2.UserID
and 
(PatIndex('%' + u1.UserID  + '%', u2.UserID)  <> 0
 OR
PatIndex('%' + u2.UserID  + '%', u1.UserID)  <> 0)
order by u1.UserID

doug.funny
doug.funny2
tim.smith
tim.smith1
tim.smith20
1 голос
/ 11 марта 2009

Если ваши данные точно такие, как вы описали, вы можете просто обрезать цифры до конца, прежде чем упорядочить их, но я подозреваю, что ваши реальные данные сложнее, чем это.

Возможно, вы захотите взглянуть на функции sql server SOUNDEX и DIFFERENCE . Это может быть не совсем то, что вам нужно, но, вероятно, вы приблизитесь

0 голосов
/ 31 марта 2009

В SSIS есть функции, которые помогут вам справиться с этим. Запустите таблицу через SSIS, и она покажет таблицы и совпадения, а также их соответствие.

0 голосов
/ 11 марта 2009

Или вы можете написать функцию, вычисляющую расстояние Левенштейна (http://de.wikipedia.org/wiki/Levenshtein-Distanz)), а затем присоединиться к таблице с помощью выражения типа

levenshtein(a.username, b.username) <=2

Вы можете удалить любые специальные символы из имен до этого, если вы считаете такие символы «мусором»

0 голосов
/ 11 марта 2009

Необычный UDF на основе расстояния Левенштейна:

CREATE FUNCTION edit_distance(@s1 nvarchar(3999), @s2 nvarchar(3999))
RETURNS int
AS
BEGIN
  DECLARE @s1_len int, @s2_len int, @i int, @j int, @s1_char nchar, @c int, @c_temp int,
    @cv0 varbinary(8000), @cv1 varbinary(8000)
  SELECT @s1_len = LEN(@s1), @s2_len = LEN(@s2), @cv1 = 0x0000, @j = 1, @i = 1, @c = 0
  WHILE @j <= @s2_len
    SELECT @cv1 = @cv1 + CAST(@j AS binary(2)), @j = @j + 1
  WHILE @i <= @s1_len
  BEGIN
    SELECT @s1_char = SUBSTRING(@s1, @i, 1), @c = @i, @cv0 = CAST(@i AS binary(2)), @j = 1
    WHILE @j <= @s2_len
    BEGIN
      SET @c = @c + 1
      SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j-1, 2) AS int) +
        CASE WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END
      IF @c > @c_temp SET @c = @c_temp
      SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j+1, 2) AS int)+1
      IF @c > @c_temp SET @c = @c_temp
      SELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1
    END
    SELECT @cv1 = @cv0, @i = @i + 1
  END
  RETURN @c
END

(Записано на форумах SQLTeam.com.)

К сожалению, это работает только тогда, когда предоставлено две строки, и за время O (нм), где n и m - длина двух строк. Тем не менее, вы могли бы улучшить это, изменив функцию, чтобы вырезать как «НЕТ МАТЧ», когда @c больше, чем, скажем, 3.

Но тогда вам все еще нужен курсор, чтобы пройтись по каждому имени пользователя в таблице и сравнить его с любым другим пользователем. Не очень эффективно, это точно.

0 голосов
/ 11 марта 2009

Было бы полезно, если бы вы точно определили, что вы подразумеваете под «подобным». Всегда ли записи соответствуют определенному шаблону, например, «буквы, точка, буквы, дополнительные числа»?

Если вы ищете опечатки или возможные орфографические ошибки, вы можете попробовать алгоритмы нечеткого сопоставления строк, такие как soundex или Levenshtein, расстояние редактирования.

0 голосов
/ 11 марта 2009

Не могли бы вы просто сделать

select * from USERTABLE order by USERNAME;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...