2010-07-21 4 views
0

У меня есть этот родитель-потомокГруппирование родителей, содержащие один и тот же набор детей

Paragraph 
--------- 
ParagraphID PK 
// other attributes ... 


Sentence 
-------- 
SentenceID PK 
ParagraphID FK -> Paragraph.ParagraphID 
Text   nvarchar(4000) 
Offset  int 
Score  int 
// other attributes ... 

я хотел бы найти пункты, которые эквивалентны; это абзацы, содержащие один и тот же набор предложений. Два предложения считаются одинаковыми, если они имеют одинаковый текст, смещение и баллы - SentenceID/ParagraphID не являются частью сравнения, а два абзаца эквивалентны, если они содержат равный набор предложений.

Может ли кто-нибудь показать мне, как будет выглядеть запрос на равные абзацы?

EDIT: есть ca. 150K и 1.5M предложения. Результат должен включать ParagraphID и самый низкий идентификатор абзаца, который эквивалентен этому. Например. если PARAGRAPH1 и PARAGRAPH2 равны, то результат будет

ParagraphID EquivParagraphID 
1   1 
2   1 

ответ

1

Короче говоря, нужна подпись для каждого абзаца, а затем сравнить подписи. Вы не указали природу самого выхода. . Здесь, я "м возвращающая строку разделенных запятыми значений ParagraphId для каждого идентичного пункта подписи

With ParagraphSigs As 
    (
    Select P.ParagraphId 
     , Hashbytes('SHA1' 
       , (
        Select '|' + S1.Text 
         '|' + Cast(S1.Offset As varchar(10)) 
         '|' + Cast(S1.Score As varchar(10)) 
        From Sentence As S1 
        Where S1.ParagraphId = P.ParagraphId 
        Order By S1.SentenceId 
        For Xml Path('') 
        )) As Signature 
    From Paragraph As P 
    ) 
Select Stuff(
      (
      Select ', ' + Cast(PS1.ParagraphId As varchar(10)) 
      From ParagraphSigs As PS1 
      Where PS1.Signature = PS.Signature 
      For Xml Path('') 
      ), 1, 2, '') As Paragraph 
From ParagraphSigs As PS 
Group By PS.Signature 

Учитывая ваше добавление о желаемом выходе, вы можете изменить запрос следующим образом:

With ParagraphSigs As 
    (
    Select P.ParagraphId 
     , Hashbytes('SHA1' 
       , (
        Select '|' + S1.Text 
         '|' + Cast(S1.Offset As varchar(10)) 
         '|' + Cast(S1.Score As varchar(10)) 
        From Sentence As S1 
        Where S1.ParagraphId = P.ParagraphId 
        Order By S1.SentenceId 
        For Xml Path('') 
        )) As Signature 
    From Paragraph As P 
    ) 
Select P1.ParagraphId, P2.ParagraphId As EquivParagraphId 
From ParagraphSigs As P1 
    Left Join ParagraphSigs As P2 
     On P2.Signature = P1.Signature 
      And P2.ParagraphId <> P1.ParagraphId 

Очевидно, возможно, что три или четыре абзаца разделяют одну и ту же подпись, поэтому следует предупредить, что приведенные выше результаты дадут вам декартово произведение соответствующих абзацев (например, (P1, P2), (P1, P3), (P2, P1), (P2, P3), (P3, P1), (P3, P2)).

В комментариях вы ответили abo эффективный поиск последнего предложения. Поскольку у вас есть два других параметра, можно уменьшить количество подписей, генерируемых делая путем сравнения на двух ИНТ столбцов первой:

With ParagraphsNeedingSigs As 
    (
    Select P1.ParagraphId 
    From Paragraph As P1 
    Where Exists (
        Select 1 
        From Paragraph As P2 
        Where P2.ParagraphId <> P1.ParagraphId 
         And P2.Offset = P1.Offet 
         And P2.Score = P1.Score 
        ) 
    ) 
    , ParagraphSigs As 
    (
    Select P.ParagraphId 
     , Hashbytes('SHA1' 
       , (
        Select '|' + S1.Text 
         '|' + Cast(S1.Offset As varchar(10)) 
         '|' + Cast(S1.Score As varchar(10)) 
        From Sentence As S1 
        Where S1.ParagraphId = P.ParagraphId 
        Order By S1.SentenceId 
        For Xml Path('') 
        )) As Signature 
    From ParagraphsNeedingSigs As P 
    ) 
Select P.ParagraphId, P2.ParagraphId As EquivParagraphId 
From Paragraph As P 
    Left Join ParagraphSigs As P1 
     On P1.ParagraphId = P.ParagraphId 
    Left Join ParagraphSigs As P2 
     On P2.Signature = P1.Signature 
      And P2.ParagraphId <> P1.ParagraphId 
+0

Благодарим вас за это. Раньше я не видел хеш-функцию, так что это был глазник. Даже если ложное совпадение статистически незначительно, есть ли способ использовать ключ как подсказку для равных абзацев, но все же использовать сравнение предложений, чтобы быть полностью уверенным? Также см. Мое обновление о желаемом выходе. – mdma

+0

@mdma - Я обновил свой пост. Короче говоря, вы можете отфильтровать абзацы, которые не имеют потенциального соответствия, на основе двух столбцов int, а затем вычисляют хэши для тех, у кого есть потенциальное совпадение. – Thomas

+0

Мой последний запрос был очень похож на этот. Я попытался также комбинировать это с INTERSECT, чтобы отсеять хеш-коллизии и убедиться, что наборы предложений были действительно равны, но накладные расходы заняли это от нескольких секунд до нескольких минут (я никогда не позволяю ему завершать.) Вместо этого я изменил хэш на ссылайтесь на фактические данные, чтобы они были уникальными. – mdma

1

Поскольку вы SQL 2008 в списке (я не уверен, что если этот синтаксис был доступен в 2005 году), вы могли бы использовать сравнения EXCEPT или INTERSECT. Это связано с коррелированными подзапросами, поэтому производительность может быть проблемой.

SELECT 
    * 
FROM 
    Paragraph P 
WHERE 
    (SELECT COUNT(*) FROM 
(
    SELECT 
     S1.[Text], 
     S1.Offset, 
     S1.Score 
    FROM 
     Paragraph P1 
    INNER JOIN Sentence S1 ON 
     S1.ParagraphID = P1.ParagraphID 
    WHERE 
     P1.ParagraphID = P.ParagraphID 
    INTERSECT 
    SELECT 
     S2.[Text], 
     S2.Offset, 
     S2.Score 
    FROM 
     Paragraph P2 
    INNER JOIN Sentence S2 ON 
     S2.ParagraphID = P2.ParagraphID 
    WHERE 
     P2.ParagraphID > P.ParagraphID 
) SQ 
) = (SELECT COUNT(*) FROM Sentence P3 WHERE P3.ParagraphID = P.ParagraphID) 
+0

Спасибо за это. Я попытался выполнить запрос, но остановил его через 5 минут. Есть ли способ улучшить работу? Я добавил размер таблицы и желаемый результат на вопрос. – mdma

Смежные вопросы