SQL Server - выбор списка ключевых слов и синонимов - PullRequest
1 голос
/ 15 апреля 2011

У меня есть две таблицы:

Ключевые слова

, где я храню уникальные ключевые слова.

CREATE TABLE [dbo].[Keywords]
[KeywordID] [int] IDENTITY(1,1) NOT NULL,
[Description] [varchar](200) NOT NULL

select * from Keywords   

  1 MVC  
  2 HTML  
  3 C#  
  4 ASP.NET MVC  
  5 MVC3

KeywordSynonymous

, где я указываю, что некоторые ключевые слова являются синонимами других.

CREATE TABLE [dbo].[KeywordSynonymous]
    [KeywordID] [int] NOT NULL,
    [KeywordSynonymousID] [int] NOT NULL

Оба поля являются FK для таблицы Ключевые слова, и оба комбинированных поля используются в качестве PK в этой таблице.

Здесь я хотел бы заявить, что «MVC» и «MVC3» являются синонимами и, возможно, «MVC3» и «ASP.NET MVC» также являются синонимами.

select * from KeywordSynonymous  

1 5  
5 4  

КОНЦЕПЦИИ

1)

Если ключевое слово «MVC» является синонимом «MVC3»
, а «MVC3» является синонимом«ASP.NET MVC»

, тогда концептуально MVC является ТАКЖЕ синонимом «ASP.NET MVC»

2)

Если ключевое слово 'MVC' является синонимом 'MVC3'

, то это также верно VICEVERSA и что MVC3 является синонимом'MVC'

ВОПРОС

Представьте, что на моем веб-сайте выполняется поиск, и пользователь может вводить что угодно, но для нашего примера онмог бы набрать 'MVC' или 'MVC3' ...

Как я могу получить с одним оператором SQL ВСЕ возможное синоним, гарантирующее, что оба понятия 1 и 2 выполнены?

Это означает, что:

>> if the user types 'MVC',        my sql should return 'MVC, MVC3', 'ASP.NET MVC'.  
>> if the user types 'MVC3',       my sql should return 'MVC, MVC3', 'ASP.NET MVC'.  
>> if the user types 'ASP.NETMVC', my sql should return 'MVC, MVC3', 'ASP.NET MVC'.  

===============================================================
ОБНОВЛЕНИЕ
Я чувствую, что должен добавить немного осайт, который я разрабатываю.Это рынок, где молодые специалисты смогут продавать свои услуги, используя новые способы продвижения.

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

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

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

=====================================================================
STACKOVERFLOW TAGS
Модуль ключевого слова должен работать очень похоже на теги StackOverflow(Ключевые слова), где, если я установлю TAGS на SQL, вы, ребята, которые ищете TSQL или SQL SERVER ... также должны увидеть этот пост.

:-)

Ответы [ 5 ]

3 голосов
/ 15 апреля 2011

Вы обязательно должны использовать Common Table Expressions. Это идеальное решение для вашей проблемы, потому что оно не меняет вашу текущую схему БД, и, самое главное, CTE является элегантным и логичным решением из-за рекурсивности, которой обладает ваша таблица KeywordSynonymous.

Для этого лучше сначала создать представление, которое выбирает все строки в KeywordSynonymous в обоих направлениях. В вашем случае эта таблица возвращает строки

select * from KeywordSynonymous  

1 5  
5 4  

и то, что сделает вид ниже, покажет

select * from KeywordSynonymousAll   

1 5      0   
2 NULL   0
3 NULL   0
4 NULL   0
4 5      1
5 1      1
5 4      0

Это представление представляет собой структуру данных, которая упростит рекурсивный запрос. Он добавляет третий столбец, чтобы определить, когда была произведена реверсия. Это необходимо для удовлетворения вашей концепции № 2.

Итак, вот оно:

create view KeywordSynonymousAll as
    select KeywordID, KeywordSynonymousID, 0 as reversed
      from KeywordSynonymous
     union
    select K.KeywordID, null as KeywordSynonymousID, 0 as reversed
      from Keywords K
     where not exists(select null
                        from KeywordSynonymous
                       where KeywordID = K.KeywordID)
     union
     select KeywordSynonymousID, KeywordID, 1 as reversed
       from KeywordSynonymous

И запрос

declare @search varchar(200);

set @search = 'MVC3'; -- TEST HERE for different search keywords

with Synonymous (keywordID, SynKeywordID) as ( 

    -- initial state: Get the keywordId and KeywordSynonymousID for the description as @search
    select K.keywordID, KS.KeywordSynonymousID
      from Keywords K
     inner join KeywordSynonymous KS on KS.KeywordID = K.keywordId
     where K.Description = @search

    union all

    -- also initial state but with reversed columns (because we want lookup in both directions)
    select KS.KeywordSynonymousID, K.keywordID
      from Keywords K
     inner join KeywordSynonymous KS on KS.KeywordSynonymousID = K.keywordId
     where K.Description = @search

     union all

    select S.SynKeywordID, KS.KeywordSynonymousID
      from Synonymous S
     inner join KeywordSynonymousAll KS on KS.KeywordID = S.SynKeywordID
     where KS.reversed = 0 -- to avoid infinite recursion

     union all

    select KS.KeywordSynonymousID, S.SynKeywordID 
      from Synonymous S
     inner join KeywordSynonymousAll KS on KS.KeywordID = S.KeywordID
     where KS.reversed = 1 -- to avoid infinite recursion

) 

-- finally output the result
select distinct K.Description
  from Synonymous S
 inner join Keywords K on K.KeywordID = S.keywordID

Для set @search = 'MVC3' набор результатов равен

  ASP.NET MVC
  MVC
  MVC3

Тот же набор результатов происходит для set @search = 'MVC' и set @search = 'ASP.NET MVC'

Для set @search = 'C#' и set @search = 'HTML' вы ничего не получите

EDIT

В моем предыдущем посте я говорил, что набор результатов будет пустым для C # и HTML. Если вы также хотите вернуть эти значения, измените последнюю часть запроса на:

-- finally output the result
select distinct T.Description
  from (
    select K.Description
      from Synonymous S
     inner join Keywords K on K.KeywordID = S.keywordID

    union

    select Description
      from Keywords
     where Description = @search) T

Теперь для set @search = 'C#' набор результатов равен

.
  C#

и для set @search = 'HTML' набор результатов равен

  HTML

Надеюсь, это поможет

2 голосов
/ 15 апреля 2011

Из-за ваших условий (понятия) , таблица синонимов не нормализована . Это основной источник вашей проблемы и сложные запросы / триггеры, необходимые для ее решения.

Я бы сохранил таблицу ключевых слов:

CREATE TABLE [dbo].[Keywords]
[KeywordID] [int] IDENTITY(1,1) NOT NULL,
[Description] [varchar](200) NOT NULL

select * from Keywords   

  1 MVC  
  2 HTML  
  3 C#  
  4 ASP.NET MVC  
  5 MVC3
  6 C sharp

и составьте синонимную таблицу по-другому:

CREATE TABLE [dbo].[KeywordSynonymity]
    [SynonymityID] [int] NOT NULL,
    [KeywordID] [int] NOT NULL

select * from KeywordSynonymous  

1 1               --- for the 1 (MVC) and 5 (MVC3)
1 5               --- being synonymous
2 3               --- for the 3 (C#) and 6 (C sharp)
2 6               --- being synonymous

Затем, чтобы добавить, что MVC3 и ASP.NET MVC также являются синонимами, вам просто нужно добавить строку (1,4) в таблицу синонимов.

Если тогда - по неизвестным причинам, но давайте все же предположим, что - вы хотите объединить MVC3 и C# как синонимы, вам придется изменить все строки с SynonymityID = 2 (синонимично для C #) на = 1 ( синоним MVC).

Но все ваши запросы будут проще, поскольку таблица нормализована.

2 голосов
/ 15 апреля 2011

Чтобы достичь хотя бы # 1, вы можете использовать рекурсивные выражения общих таблиц (CTE)
с определением здесь

1 голос
/ 15 апреля 2011

1 называется Симметричное отношение , а 2 называется Переходное отношение .

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

Вот хранимая процедура для добавления новых ключевых слов следующим образом:

CREATE PROCEDURE [dbo].[AddKeyword] 
    @newKeyword [varchar](200), 
    @synonymKeyword [varchar](200) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    set transaction isolation level serializable

    begin transaction

        if EXISTS (select 1 from Keywords where [Description] = @newKeyword)
        begin
            commit transaction
            return
        end

        declare @masterKeywordId int

        select 
            @masterKeywordId = ISNULL(KeywordSynonymous.KeywordID, Keywords.KeywordID) 
        from
            Keywords
        left join
            KeywordSynonymous
        on
            Keywords.KeywordID = KeywordSynonymous.KeywordSynonymousID
        where
            [Description] = @synonymKeyword

        insert into Keywords VALUES (@newKeyword)

        if @masterKeywordId is not null
            insert into KeywordSynonymous VALUES (@masterKeywordId,SCOPE_IDENTITY())

    commit transaction

END

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

И вот как вы выбираете их все в конце:

CREATE PROCEDURE [dbo].[GetSynonymKeywords]
    @keyword [varchar](200)
AS
BEGIN
    SET NOCOUNT ON;

    declare @masterKeywordId int

    select 
        @masterKeywordId = ISNULL(KeywordSynonymous.KeywordID, Keywords.KeywordID) 
    from
        Keywords
    left join
        KeywordSynonymous
    on
        Keywords.KeywordID = KeywordSynonymous.KeywordSynonymousID
    where
        [Description] = @keyword

    select 
        KeywordId,[Description]
    from
        Keywords
    where
        KeywordId = @masterKeywordId
    union
    select 
        Keywords.KeywordId,[Description]
    from
        KeywordSynonymous
    join
        Keywords
    on
        KeywordSynonymous.KeywordSynonymousID = Keywords.KeywordId
    where
        KeywordSynonymous.KeywordId = @masterKeywordId

END

Эта хранимая процедура сначала находит идентификатор ключевого слова по переданному ключевому слову.Затем он ищет ключевое слово "master" для этого идентификатора.Затем он возвращает ключевое слово master и все ключевые слова, которые являются синонимами к этому ключевому слову master.

Пример добавления новых слов:

EXEC [dbo].[AddKeyword] @newKeyword = N'MVC'
EXEC [dbo].[AddKeyword] @newKeyword = N'ASP.NET MVC',   @synonymKeyword = 'MVC'
EXEC [dbo].[AddKeyword] @newKeyword = N'MVC3',  @synonymKeyword = 'ASP.NET MVC'

Обратите внимание, что в третьей строке вы могли указать 'MVC'как синоним, он работал бы так же хорошо.

Пример получения ключевых слов:

[dbo].[GetSynonymKeywords]  @keyword = N'MVC3'
[dbo].[GetSynonymKeywords]  @keyword = N'ASP.NET MVC'
[dbo].[GetSynonymKeywords]  @keyword = N'MVC3'

Все три возвращают один и тот же список значений.

I'Поместив уровень изоляции в сериализованный в AddKeyword SP, чтобы убедиться, что нет проблем с параллелизмом, не стесняйтесь изменять его в соответствии с вашей моделью параллелизма, сериализированный может не подойти вам.

Вы также можете извлечь GetMasterId (блок, который появляется в обоих SP) в UDF, если вы этого хотите, или внесите любую другую модификацию, соответствующую вашему конкретному сценарию.

0 голосов
/ 18 апреля 2011

ОК, как насчет этого:

DECLARE @TempKeywordID TABLE (KeywordID int)
INSERT INTO @TempKeywordID (KeywordID)(select KeywordID from Keywords where [Description] = @SearchKeyword)

DECLARE @intFlag INT
SET @intFlag = 1

WHILE (@intFlag <=(Select Count(KeywordSynonymousID) from KeywordSynonymous)) --Loop for all records in KeywordSynonymous
BEGIN
    INSERT INTO @TempKeywordID (KeywordID)(Select KeywordSynonymousID from KeywordSynonymous where KeywordID in (Select KeywordID from @TempKeywordID))
    INSERT INTO @TempKeywordID (KeywordID)(Select KeywordID from KeywordSynonymous where KeywordSynonymousID in (Select KeywordID from @TempKeywordID))    

    SET @intFlag = @intFlag + 1
END

SELECT * FROM Keywords WHERE KeywordID IN (SELECT * FROM @TempKeywordID)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...