Используя TSQL, как найти слова и группировки до и после заданного термина? - PullRequest
1 голос
/ 03 апреля 2012

Учитывая определенный шаблон слов (скажем, «воздушный шар»), я хотел бы найти количество n слов до и после, сгруппировать их по количеству, которое существует в названии моей таблицы

Например, если набор данных был:

  • небо с красным шаром
  • дорога с желтым шаром
  • кресло с голубым шаром

Я бы хотел, чтобы результаты были примерно такими:

- red balloon | 1
- yellow balloon | 1
- blue balloon | 1
- balloon sky | 2
- balloon chair | 1

Я подумал, что лучший способ добиться этого - использовать регулярные выражения в моем sproc.Итак, я добавил замечательные функции регулярных выражений, перечисленные здесь здесь , и функцию FindWordsInContext.

Для начала:

WITH Words_CTE (Title)
AS
-- Define the CTE query.
(
    SELECT Title
    FROM ItemData
    WHERE Title LIKE '%balloon%'
)
-- Define the outer query referencing the CTE name.
SELECT Title
FROM Words_CTE

Итак, я решил, что начну сэто и включите функцию FindWordsInContext в микс, затем выполните группировку по словам / перед заданным словом.

- ОБНОВЛЕНИЕ -

Спасибо Адриану Ифтоде ниже ... но код не совсем соответствует тому, что я ищу.

declare @table table(Sentence varchar(250))

insert into @table(sentence)
    values ('I have another red balloon in the car.'),
            ('Here is a new balloon for you.'),
            ('A red balloon is in the other room.'),
            ('Is there another balloon for me?')


select TOP(5) SentencePart, NumberOfWords
from @table
cross apply dbo.fnGetPartsFromSentence(Sentence, 'balloon') f
order by
  NumberOfWords DESC,
  case when f.Side = 'R' then 0
  else 1 end

Выходы:

balloon is in the other room.       5
I have another red balloon          4
Here is a new balloon               4
Is there another balloon            3
balloon in the car.                 3

Я хотел бы иметь возможность установить диапазон по обе стороны от "воздушного шара".В этом случае, скажем, одним словом, вывод должен быть:

red balloon      2
new balloon      1
another balloon  1
balloon in       1
balloon for      2
balloon is       1

1 Ответ

0 голосов
/ 05 апреля 2012

Немного кода, я постараюсь объяснить

Сначала я использовал функцию split, собираюсь разделить varchar по данному varchar

CREATE FUNCTION [dbo].[fnSplitString](@str NVARCHAR(MAX),@sep NVARCHAR(MAX))
RETURNS TABLE
AS
RETURN
    WITH a AS(
        SELECT CAST(0 AS BIGINT) AS idx1,
               CHARINDEX(@sep,@str) idx2, 
               1 as [Level]
        UNION ALL
        SELECT idx2 + coalesce(nullif(LEN(@sep),0),1),
               CHARINDEX(@sep,@str, idx2 + 1), 
               [Level] + 1 as [Level]
        FROM a
        WHERE idx2 > 0
    )
    SELECT SUBSTRING(@str,idx1,COALESCE(NULLIF(idx2,0),LEN(@str)+1)-idx1) AS Value, 
           [Level], 
           case when idx1 = 0 then 'R' when idx2 != 0 then 'LR' else 'L' end as Side
    FROM a  

Учитывая varchar 'красное воздушное шариковое небо' , и когда разделение является пробелом, оно выведет:

select *
from dbo.fnSplitString('red balloon sky', ' ')

Value   Level   Side
red      1       R
balloon  2       LR
sky      3       L

Сторона означает: если R, то пробел находится с правой стороны слова, если L, то пробел с левой стороны слова, а если LR, то слово окружено пробелами.

Когда разделение является «воздушный шар»

select *
from dbo.fnSplitString('red balloon sky', 'balloon')

red     1   R
 sky    2   L

Таким образом, шар появляется с правой стороны красный и появляется с левой стороны небо

Имея эту полезную функцию, я создал еще одну функцию, которая выведет необходимый формат для одного предложения (varchar)

create FUNCTION [dbo].[fnGetPartsFromSentence](@sentence NVARCHAR(MAX),@word NVARCHAR(MAX))
RETURNS TABLE
AS
RETURN


with RawData as
(select rtrim(ltrim(f.Value)) as LR, 
       (select COUNT (*) from dbo.fnSplitString(rtrim(ltrim(f.Value)), ' ')) as NumberOfWords,
       f.Side,
       0 as SideLevel
from dbo.fnSplitString(@sentence, @word) as f
where f.Side = 'R' or f.Side = 'L'
union all
(
    select rtrim(ltrim(f.Value)) as LR, 
       (select COUNT (*) from dbo.fnSplitString(rtrim(ltrim(f.Value)), ' ')) as NumberOfWords,
       f.Side,
       sl.no as SideLevel
    from dbo.fnSplitString(@sentence, @word) as f
    join (select 1 as no union all select 2) sl on 1 = 1
    where f.Side = 'LR'
)
)
select (case when Side = 'R' then LR + ' ' + @word 
             when Side = 'L' then @word + ' ' + LR
             when Side = 'LR' then  
                    (
                        case when SideLevel  = 1 then @word + ' ' + LR
                        when SideLevel  = 2 then LR + ' ' + @word 
                        end
                    )
            end) as SentencePart,
        (case when Side = 'R' or Side = 'L' then Side
              else           
                   (    case when SideLevel  = 1 then 'L'
                        when SideLevel  = 2 then 'R'
                        end
                    )
            end) as Side,
        NumberOfWords           
from RawData

Эта функция использует предыдущую. Сначала он разбивает предложение по словам и считает слова в разделениях, выполняя еще одно разделение по пробелам. Когда слово появляется с обеих сторон разделения, оно повторяет разделение (которое объединяется с 1, 2 значениями).

Эта функция также будет выводить разделение, соединенное со словом, в зависимости от того, с какой стороны оно находится: слева, справа или оба. Он также выведет сторону, на этот раз влево или вправо.

select *
from [dbo].[fnGetPartsFromSentence]('yellow balloon sky road','balloon')

SentencePart        Side    NumberOfWords

yellow balloon      R           1
balloon sky road    L           2

Теперь, используя эту функцию, я могу скрестить ее с таблицей

declare @table table(Sentence varchar(250))

insert into @table(sentence)
    values ('red balloon sky'),
            ('yellow balloon sky road'),
            ('blue balloon chair')


select SentencePart, NumberOfWords
from @table
cross apply dbo.fnGetPartsFromSentence(Sentence, 'balloon') f
order by
  case when f.Side = 'R' then 0
  else 1 end

Выход

red balloon           1
yellow balloon        1
blue balloon          1
balloon chair         1
balloon sky road      2
balloon sky           1

Работает и при наличии нескольких случаев

...