Проблема с алгоритмом SQL - цикл для добавления и фильтрации результатов - PullRequest
0 голосов
/ 09 июля 2009

Я снова борюсь с SQL с помощью функции поиска.

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

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

Так вот мой алгоритм

if null return all restaurants

declare results = empty

for each word
if in restaurant.name or cuisine.name
    addRowsToResults 
else if in restaurant.city
    addRowsToResults 
else if in restaurant.postcode
    addRowsToResults 


addRowsToResults 
results = (results + new results) where a match is in both

Я понятия не имею, с чего начать, и я искал в Google целую вечность, но, будучи новичком в SQL, я могу пропустить некоторые термины.

И это даже лучший способ сделать что-то подобное?

Ценю любую помощь.

Edit: Просто чтобы дать больше информации. Город, почтовый индекс и имя - все поля nvarchar в таблице Restaurants. Название кухни находится в другой таблице и связано через другую таблицу с ресторанами, поскольку ресторан может обслуживать многие типы кухни.

Все эти поля имеют индексацию полнотекстового поиска.

Ответы [ 7 ]

1 голос
/ 09 июля 2009

Похоже, что вы пытаетесь использовать процедурный подход к основанному на множестве декларативному языку запросов.

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

CREATE function [dbo].[csl_to_table] ( @list nvarchar(MAX) )
RETURNS @list_table TABLE ([id] nvarchar(20)) -- set this to your maximum size string
AS
BEGIN
    DECLARE     @index INT,
                @start_index INT,
                @id nvarchar(20)  -- set this to your maximum size string

    SELECT @index = 1 
    SELECT @start_index = 1
    WHILE @index <= DATALENGTH(@list)
    BEGIN

        IF SUBSTRING(@list,@index,1) = ','
        BEGIN

                SELECT @id = SUBSTRING(@list, @start_index, @index - @start_index) 
                INSERT @list_table ([id]) VALUES (@id)
                SELECT @start_index = @index + 1
        END
        SELECT @index  = @index + 1
    END
    SELECT @id = SUBSTRING(@list, @start_index, @index - @start_index )
    INSERT @list_table ([id]) VALUES (@id)
    RETURN
END

Затем, используя свой набор результатов, вы можете присоединиться к таблицам, для которых вы хотите запросить совпадения, и вернуть набор результатов совпадений.

1 голос
/ 10 июля 2009

Вот функция разделения, которая имеет необязательный параметр разделителя ...

CREATE FUNCTION [dbo].[fnSplit]( @List VARCHAR(4000), @Delimiter CHAR(1) = ',' )
    RETURNS @Result TABLE (item VARCHAR(100))
    BEGIN
        DECLARE @Item VARCHAR(100)
        WHILE CHARINDEX(@Delimiter,@List,0) <> 0
        BEGIN
            SELECT @Item = SUBSTRING(@List,1,CHARINDEX(@Delimiter,@List,0)-1)
                , @List = SUBSTRING(@List,CHARINDEX(@Delimiter,@List,0)+1,LEN(@List))

            IF LEN(@Item) > 0
                INSERT INTO @Result
                    SELECT @Item
        END
        IF LEN(@List) > 0
            INSERT INTO @Result
                SELECT @List
        RETURN
    END
GO

Тогда ваш запрос может выглядеть примерно так ...

SELECT DISTINCT
      'restaurants' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name + '(' + r.city + ', ' + r.state + ')' AS [Description]
FROM restaurants AS [r]
JOIN [dbo].[fnSplit](@Query,' ') AS [terms]
ON
    ISNULL(r.name,'')
    +','+ISNULL(r.city,'')
    +','+ISNULL(r.postcode,'') LIKE '%'+terms.item+'%'

UNION SELECT DISTINCT
      'cuisine' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name AS [Description]
FROM cuisine AS [r]
JOIN [dbo].[fnSplit](@Query,' ') AS [terms]
ON
    ISNULL(r.name,'') LIKE '%'+terms.item+'%'

Функция fnSplit разбивает ваши условия запроса на строки в табличной переменной и возвращает их. Затем запросы на выборку объединяются с результирующей таблицей. Запрос отличается тем, что возвращается только 1 экземпляр строки независимо от того, сколько терминов соответствует запросу. Условие соединения можно легко разбить на ряд условий И / ИЛИ, но я думаю, что операции LIKE дороже, чем конкатенация, поэтому я просто конкатенирую столбцы.

UPDATE

Можно использовать следующий упрощенный запрос, поскольку полнотекстовая индексация включена.

DECLARE @Phrase VARCHAR(4000)
SELECT @Phrase = item + '" OR "' FROM [dbo].[fnSplit](@Query,' ')
SET @Phrase = SUBSTRING(@Phrase,0,LEN(@Phrase)-6)


SELECT DISTINCT
      'restaurants' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name + '(' + r.city + ', ' + r.state + ')' AS [Description]
FROM restaurants AS [r]
WHERE
    CONTAINS(r.name,@Phrase)
    OR CONTAINS(r.city,@Phrase)
    OR CONTAINS(r.postcode,@Phrase)

UNION SELECT DISTINCT
      'cuisine' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name AS [Description]
FROM cuisine AS [r]
WHERE CONTAINS(r.name,@Phrase)
1 голос
/ 09 июля 2009

Здесь есть функция разделения на слова , функция . Который возвращает таблицу, к которой вы можете присоединиться.

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

1 голос
/ 09 июля 2009

Это не совсем так, как работает SQL. Вы можете получить некоторые из тех же функций, но они не будут выглядеть так, как вы просите; Вы пишете (достаточно естественно) в более процедурном стиле.

С одной стороны, «добавить результаты» - попытка объединить (предположительно) разрозненные вещи в одну коллекцию. Вы - я думаю - просите, чтобы все рестораны и кухни, города и почтовые индексы входили в одну и ту же коллекцию, и единственным видом SQL-коллекции (грубо говоря) являются таблицы, а внутри таблицы все строки имеют одинаковый тип.

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

SELECT * 
FROM   your_table
WHERE  restaurant like "%" + word + "%"
OR     cuisine    like "%" + word + "%"
OR     city       like "%" + word + "%"
OR     postcode   like "%" + word + "%";

Это начинает усложняться, если вы хотите сопоставить несколько слов; этот ответ предназначен в качестве отправной точки; возможно, когда вы лучше познакомитесь с SQL, вы сможете задать более простые вопросы. Удачи!

Обновление на основе описания вашей таблицы

SELECT r.*
FROM       restaurant r
INNER JOIN link       k ON k.restaurant_id = r.restaurant_id
INNER JOIN cuisine    c on c.cuisine_id    = k.cuisine_id
WHERE  r.restaurant like "%" + word + "%"
OR     c.name       like "%" + word + "%"
OR     r.city       like "%" + word + "%"
OR     r.postcode   like "%" + word + "%";

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

0 голосов
/ 09 июля 2009

Чтобы использовать полнотекстовый поиск, вы хотите использовать ключевое слово CONTAINS. Посмотрите, как использовать его в Книгах онлайн (мне нужно попасть на собрание или я приведу пример).

0 голосов
/ 09 июля 2009

Если вы ищете цикл «Для каждого» в SQL, большинство диалектов SQL имеют курсоры, которые позволяют вам выбрать все записи, используя некоторый запрос, а затем выполнить итерацию по записям, используя курсор.

0 голосов
/ 09 июля 2009

Даже если бы я захотел, я не смог бы рассказать вам об этом полностью - вы не опубликовали определения таблиц или какие-либо примеры данных.

Тем не менее, я сделаю некоторые догадки. Прежде всего, я думаю, что вам нужно использовать SQL на основе множеств. Не думайте с точки зрения обработки строка за строкой - обрабатывайте набор. Затем объедините наборы вместе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...