Если у меня есть два полнотекстовых индекса для таблиц, таких как Contacts
и Companies
, как я могу написать запрос, который гарантирует, что ВСЕ слова поисковой фразы существуют в либо два индекса?
Например, если я ищу контакты, в которых все ключевые слова существуют либо в записи контакта, либо в компании, как мне написать запрос?
Я попытался выполнить CONTAINSTABLE
для таблиц контактов и компании, а затем соединить таблицы вместе, но если я передам поисковую фразу каждому как '"searchTerm1*' AND '"searchTerm2*"'
, тогда она будет совпадать только тогда, когда all искомые слова находятся в обоих индексах и возвращают слишком мало записей. Если я передаю его как '"searchTerm1*' OR '"searchTerm2*"'
, то оно совпадает с тем, где любой (вместо все ) поисковых слов находится в или из индексы и возвращает слишком много записей.
Я также попытался создать индексированное представление, объединяющее контакты с компаниями, чтобы можно было искать по всем столбцам за один раз, но, к сожалению, контакт может принадлежать нескольким компаниям, и поэтому ContactKey, который я собирался использовать в качестве ключа для представление больше не является уникальным и поэтому не может быть создано.
Кажется, может быть, мне нужно разбить фразу на части и запросить каждое слово отдельно, а затем объединить результаты вместе, чтобы убедиться, что все слова были сопоставлены, но я не могу думать о том, как написать этот запрос ,
Вот пример того, как модель может выглядеть:
Contact CompanyContact Company
-------------- -------------- ------------
ContactKey ContactKey CompanyKey
FirstName CompanyKey CompanyName
LastName
У меня есть полнотекстовый индекс на FirstName, LastName и другой на CompanyName.
2 ответа
Я создал метод, который работает с любым количеством полнотекстовых индексов и столбцов. Используя этот метод, очень легко добавить дополнительные аспекты для поиска.
- Разбить поисковую фразу на строки во временной таблице
- Присоединитесь к этой временной таблице, чтобы выполнить поиск по каждому поисковому запросу, используя CONTAINSTABLE для каждого применимого полнотекстового индекса.
- Объедините результаты вместе и получите различное количество найденных поисковых терминов.
- Отфильтруйте результаты, если указанное количество поисковых терминов не соответствует количеству найденных поисковых запросов.
Примере:
DECLARE @SearchPhrase nvarchar(255) = 'John Doe'
DECLARE @Matches Table(
MentionedKey int,
CoreType char(1),
Label nvarchar(1000),
Ranking int
)
-- Split the search phrase into separate words.
DECLARE @SearchTerms TABLE (Term NVARCHAR(100), Position INT)
INSERT INTO @SearchTerms (Term, Position)
SELECT dbo.ScrubSearchTerm(Term)-- Removes invalid characters and convert the words into search tokens for Full Text searching such as '"word*"'.
FROM dbo.SplitSearchTerms(@SearchPhrase)
-- Count the search words.
DECLARE @numSearchTerms int = (SELECT COUNT(*) FROM @SearchTerms)
-- Find the matching contacts.
;WITH MatchingContacts AS
(
SELECT
[ContactKey] = sc.[KEY],
[Ranking] = sc.[RANK],
[Term] = st.Term
FROM @SearchTerms st
CROSS APPLY dbo.SearchContacts(st.Term) sc -- I wrap my CONTAINSTABLE query in a Sql Function for convenience
)
-- Find the matching companies
,MatchingContactCompanies AS
(
SELECT
c.ContactKey,
Ranking = sc.[RANK],
st.Term
FROM @SearchTerms st
CROSS APPLY dbo.SearchCompanies(st.Term) sc
JOIN dbo.CompanyContact cc ON sc.CompanyKey = cc.CompanyKey
JOIN dbo.Contact c ON c.ContactKey = cc.ContactKey
)
-- Find the matches where ALL search words were found.
,ContactsWithAllTerms AS
(
SELECT
c.ContactKey,
Ranking = SUM(x.Ranking)
FROM (
SELECT ContactKey, Ranking, Term FROM MatchingContacts UNION ALL
SELECT ContactKey, Ranking, Term FROM MatchingContactCompanies
) x
GROUP BY c.ContactKey
HAVING COUNT(DISTINCT x.Term) = @numSearchTerms
)
SELECT
*
FROM ContactsWithAllTerms c
< Сильный > Обновление Согласно комментариям, вот пример моей функции SearchContacts
. Это простая функция-обертка, потому что я использовал ее в нескольких процедурах.
CREATE FUNCTION [dbo].[SearchContacts]
(
@contactsKeyword nvarchar(4000)
)
RETURNS @returntable TABLE
(
[KEY] int,
[RANK] int
)
AS
BEGIN
INSERT @returntable
SELECT [KEY],[RANK] FROM CONTAINSTABLE(dbo.Contact, ([FullName],[LastName],[FirstName]), @contactsKeyword)
RETURN
END
GO
Этот ответ перестроен для решения вашей проблемы, так что по полям должно существовать несколько строк. Обратите внимание на один ключ в таблице ссылок CompanyContactLink:
CREATE FULLTEXT CATALOG CompanyContact WITH ACCENT_SENSITIVITY = OFF
GO
CREATE TABLE Contact ( ContactKey INT IDENTITY, FirstName VARCHAR(20) NOT NULL, LastName VARCHAR(20) NOT NULL )
ALTER TABLE Contact ADD CONSTRAINT PK_Contact PRIMARY KEY NONCLUSTERED ( ContactKey )
CREATE TABLE Company ( CompanyKey INT IDENTITY, CompanyName VARCHAR(50) NOT NULL )
ALTER TABLE Company ADD CONSTRAINT PK_Company PRIMARY KEY NONCLUSTERED ( CompanyKey )
GO
CREATE TABLE CompanyContactLink ( CompanyContactKey INT IDENTITY NOT NULL, CompanyKey INT NOT NULL, ContactKey INT NOT NULL )
GO
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Dipper', 'Pines' )
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Mabel', 'Pines' )
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Stanley', 'Pines' )
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Soos', 'Ramirez' )
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Wendy', 'Corduroy' )
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Sheriff', 'Blubs' )
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Bill', 'Cipher' )
INSERT INTO Contact ( FirstName, LastName ) VALUES ( 'Pine Dip', 'Nobody' )
INSERT INTO Contact ( FirstNAme, LastName ) VALUES ( 'Nobody', 'Pine Dip' )
INSERT INTO Company ( CompanyName ) VALUES ( 'Mystery Shack' )
INSERT INTO Company ( CompanyName ) VALUES ( 'Greesy Diner' )
INSERT INTO Company ( CompanyName ) VALUES ( 'Watertower' )
INSERT INTO Company ( CompanyName ) VALUES ( 'Manotaur Cave' )
INSERT INTO Company ( CompanyName ) VALUES ( 'Big Dipper Watering Hole' )
INSERT INTO Company ( CompanyName ) VALUES ( 'Lost Pines Dipping Pool' )
GO
INSERT INTO CompanyContactLink Values (3, 5), (1, 1), (1, 2), (1, 3), (1, 4), (1,5), (5,1), (3,1), (4,1)
GO
CREATE FULLTEXT INDEX ON Contact (LastName, FirstName)
KEY INDEX PK_Contact
ON CompanyContact
WITH STOPLIST = SYSTEM
CREATE FULLTEXT INDEX ON Company (CompanyName)
KEY INDEX PK_Company
ON CompanyContact
WITH STOPLIST = SYSTEM
GO
CREATE VIEW CompanyContactView
WITH SCHEMABINDING
AS
SELECT
CompanyContactKey,
CompanyName,
FirstName,
LastName
FROM
dbo.CompanyContactLink
INNER JOIN dbo.Company ON Company.CompanyKey = CompanyContactLink.CompanyKey
INNER JOIN dbo.Contact ON Contact.ContactKey = CompanyContactLink.ContactKey
GO
CREATE UNIQUE CLUSTERED INDEX idx_CompanyContactView ON CompanyContactView (CompanyContactKey);
GO
CREATE FULLTEXT INDEX ON CompanyContactView (CompanyName, LastName, FirstName)
KEY INDEX idx_CompanyContactView
ON CompanyContact
WITH STOPLIST = SYSTEM
GO
-- Wait a few moments for the FULLTEXT INDEXing to take place.
-- Check to see how the index is doing ... repeat the following line until you get a zero back.
DECLARE @ReadyStatus INT
SET @ReadyStatus = 1
WHILE (@ReadyStatus != 0)
BEGIN
SELECT @ReadyStatus = FULLTEXTCATALOGPROPERTY('CompanyContact', 'PopulateStatus')
END
SELECT
CompanyContactView.*
FROM
CompanyContactView
WHERE
FREETEXT((FirstName,LastName,CompanyName), 'Dipper') AND
FREETEXT((FirstName,LastName,CompanyName), 'Shack')
GO
И ради вашего примера с Венди в Watertower:
SELECT
CompanyContactView.*
FROM
CompanyContactView
WHERE
FREETEXT((FirstName,LastName,CompanyName), 'Wendy') AND
FREETEXT((FirstName,LastName,CompanyName), 'Watertower')
GO
Новые вопросы
sql
Язык структурированных запросов (SQL) - это язык запросов к базам данных. Вопросы должны включать примеры кода, структуру таблицы, примеры данных и тег для используемой реализации СУБД (например, MySQL, PostgreSQL, Oracle, MS SQL Server, IBM DB2 и т. Д.). Если ваш вопрос относится исключительно к конкретной СУБД (использует определенные расширения / функции), используйте вместо этого тег этой СУБД. Ответы на вопросы, помеченные SQL, должны использовать стандарт ISO / IEC SQL.