Поисковый запрос - поиск по нескольким таблицам и столбцам - PullRequest
0 голосов
/ 16 мая 2018

У меня есть база данных, в которой я возвращаю информацию о нескольких объектах в одном свободном текстовом поиске, вот пример базы данных:

dbo.Electrician

ElectricianId | Company     | TelNo     | Mobile   | Addr1        | Postcode
123           | Sparky 1    | 01234567  | 0789078  | 42 lower ave | Ex2345
124           | Sparky 2    | 01235678  | 0777777  | 1 Street     | Ta6547
125           | Sparky 3    | 05415644  | 0799078  | 4 Air Road   | Gl4126

dbo.Painters

PainterId     | Company     | TelNo     | Mobile   | Addr1        | Postcode
333           | Painter 1   | 01234568  | 07232444 | 4 Higher ave | Ex2345
334           | Painter 2   | 01235679  | 07879879 | 5 Street     | Ta6547
335           | Painter 3   | 05415645  | 07654654 | 5 Sky Road   | Gl4126

dbo.Clients

ClientId | Name            | TelNo     | Mobile   | Addr1        | Postcode
100333   | Mr Chester      | 0154 5478 | 07878979 | 9 String Rd  | PL41 1X
100334   | Mrs Garrix      | 0254 6511 | 07126344 | 10 String Rd | PL41 1X
100335   | Ms Indy Pendant | 0208 1154 | 07665654 | 11 String Rd | PL41 1X

Мой текущий метод работает так:

Создать временную таблицу (EntityId, DisplayName, LongName, EntityType)

Возьмите условия поиска и замените ненужные символы перед заменой пробелов запятыми и использованием этого в качестве CSV.

SET @searchTerms = LTRIM(RTRIM(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(
        REPLACE(LTRIM(RTRIM(@searchTerms)), ',', ' '),
        '[', ''),
        ']', ''),
        '#', ''),
        '&', ''),
        ';', ''),
        '?', ''),
        '`', ''),
        '''', ''),
        '*', ''),
        '"', ''),
        '<', ' '),
        '>', ' '),
        '-', ' '),
        '(', ' '),
        ')', ' '),
        '\', ' '),
        '/', ' ')))

        SET @searchTerms = REPLACE(@searchTerms, ' ', ',')

        DECLARE @SearchTerm AS nvarchar(50);

        DECLARE @DevelopmentCursor AS CURSOR;
        SET @DevelopmentCursor = CURSOR
        FOR
        SELECT
          *
        FROM general.Csvtoquery(@searchTerms)
        WHERE value != ''

Далее я перебираю условия поиска, вставляя каждую сущность в мою временную таблицу:

            INSERT INTO #tempsearchtable (EntityId, Name, LongName, EntityType)
            SELECT
                tc.ClientId,
                tc.Title + ' ' + tc.FirstName + ' ' + tc.LastName,
                tc.Title + ' ' + tc.FirstName + ' ' + tc.LastName + ', ' + COALESCE(a.NameOrNumber, '') + ', ' + COALESCE(a.Street, '') + ', ' + COALESCE(a.Town, '') + ', ' + + ', ' + COALESCE(a.County, '') + ', ' + COALESCE(a.Postcode, '') + ', ' + COALESCE(a.Country, '')  + ', ' + COALESCE(tc.EmailAddress, '')  + ', ' + COALESCE(REPLACE(tc.Telephone, ' ', ''), '')  + ', ' + COALESCE(REPLACE(tc.Mobile, ' ', ''), ''),
                'Client'
            FROM 
                dbo.Clients tc
            LEFT JOIN 
                dbo.[Address] a ON tc.AddressId = a.AddressId
            WHERE 
                tc.FirstName LIKE '%' + @SearchTerm + '%'
                OR tc.LastName LIKE '%' + @SearchTerm + '%'
                OR tc.EmailAddress = @SearchTerm
                OR REPLACE(tc.Telephone, ' ', '') LIKE '%' + @SearchTerm + '%'
                OR REPLACE(tc.Mobile, ' ', '') LIKE '%' + @SearchTerm + '%'
                OR a.NameOrNumber LIKE '%' + @SearchTerm + '%'
                OR a.Street LIKE '%' + @SearchTerm + '%'
                OR a.Postcode LIKE '%' + @SearchTerm + '%'
                OR a.County LIKE '%' + @SearchTerm + '%'
                OR a.Town LIKE '%' + @SearchTerm + '%'
                OR a.Country LIKE '%' + @SearchTerm + '%'

Теперь я снова зацикливаю свои поиски. Это сделано для того, чтобы я получал только конкретные совпадения. Я удаляю все, где LongName не содержит моего поискового запроса.

Я выбираю все из временной таблицы перед тем, как бросить ее.

Хотя это работает и работает довольно хорошо, поиск идет медленнее, чем хотелось бы, и я искал предложения, чтобы ускорить это. Одним из них было создание индексной таблицы и добавление в нее всех сущностей, и только один цикл получения определенных поисков. Это немного быстрее, но это также означает, что у меня есть данные только для тех случаев, когда была задана последняя задача для сброса данных в индекс. Живые поиски являются обязательными.

Спасибо за любые предложения.

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Я не уверен, что это будет быстрее, но вы пытались создать одну строку и использовать LIKE для этой строки?

Что-то вроде:

SELECT
  ...
FROM 
  dbo.Clients tc
  LEFT JOIN 
    dbo.[Address] a ON tc.AddressId = a.AddressId
WHERE 
  REPLACE( tc.FirstName + '|' + tc.LastName + '|' + tc.EmailAddress + tc.Telephone + '|' + ....., ' ', '' ) LIKE '%' + @SearchTerm + '%'

Учитывая, что SQL не так хорош в анализе, мне интересно, выполняет ли LIKE ленивый поиск по выражению, который мог бы сделать этот подход быстрее, чем использование заграждений операторов OR. '|' Трубные знаки запрещают поисковым терминам, таким как "jared", совпадать с FirstName + LastName из "Jar Jar", "Edwards" и т. Д.

0 голосов
/ 16 мая 2018

Сначала несколько наблюдений:

  • Если ваши таблицы имеют одинаковую структуру (как это выглядит здесь), было бы лучше не хранить разные таблицы, а хранить их все в одном с дополнительным столбцом для установки " DataType».

  • Столбец Addr1 выглядит так, как будто вы держите в своей таблице более одного адреса. Всякий раз, когда вы чувствуете необходимость нумеровать имя столбца (phone1, phone2), вам следует подумать о соответствующей вспомогательной таблице.

Вернуться к вашей проблеме:

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

Я не знаю, правильно ли я понял, но вы можете попробовать это:

Редактировать: использовано неверное имя таблицы ...

- только два ваших стола

DECLARE @tblElectrician TABLE(ElectricianId INT,Company VARCHAR(100),TelNo VARCHAR(100),Mobile VARCHAR(100),Addr1 VARCHAR(100),Postcode VARCHAR(100));
INSERT INTO @tblElectrician VALUES
 (123,'Sparky 1','01234567','0789078','42 lower ave','Ex2345')
,(124,'Sparky 2','01235678','0777777','1 Street','Ta6547')
,(125,'Sparky 3','05415644','0799078','4 Air Road','Gl4126');

DECLARE @tblPainters TABLE(PainterId INT,Company VARCHAR(100),TelNo VARCHAR(100),Mobile VARCHAR(100),Addr1 VARCHAR(100),Postcode VARCHAR(100));
INSERT INTO @tblPainters VALUES
 (333,'Painter 1','01234568','07232444','4 Higher ave','Ex2345')
,(334,'Painter 2','01235679','07879879','5 Street','Ta6547')
,(335,'Painter 3','05415645','07654654','5 Sky Road','Gl4126');

- условия поиска (после очистки)

DECLARE @SearchTerms TABLE(LookFor VARCHAR(100));
INSERT INTO @SearchTerms VALUES('Spar'),('78');

- Запрос будет начинаться с UNION для таблиц, чтобы получить их как единое целое. (Это на самом деле так, как это должно быть сохранено)

WITH UnionedData aS
(
    SELECT 'E' AS DataType, ElectricianId AS [Id],Company,TelNo,Mobile,Addr1,Postcode
    FROM @tblElectrician
    UNION ALL
    SELECT 'P',PainterId,Company,TelNo,Mobile,Addr1,Postcode
    FROM @tblPainters
) 
,AllValues AS
(
    SELECT a.*
          ,(
            SELECT b.* 
            FROM UnionedData AS b
            WHERE a.Id=b.Id
            FOR XML PATH('x'),TYPE
           ).query(N'data(/x/*)').value(N'.',N'nvarchar(max)') AS Concatenated
    FROM UnionedData AS a
)
SELECT AllValues.*
FROM AllValues
WHERE EXISTS(SELECT 1 FROM @SearchTerms AS st 
             WHERE AllValues.Concatenated LIKE '%' + st.LookFor + '%');

Промежуточный результат cte AllValues таков (только столбцы Id и Concatenated):

id     concatenated (all values as one string)
123    E 123 Sparky 1 01234567 0789078 42 lower ave Ex2345
124    E 124 Sparky 2 01235678 0777777 1 Street Ta6547
125    E 125 Sparky 3 05415644 0799078 4 Air Road Gl4126
333    P 333 Painter 1 01234568 07232444 4 Higher ave Ex2345
334    P 334 Painter 2 01235679 07879879 5 Street Ta6547
335    P 335 Painter 3 05415645 07654654 5 Sky Road Gl4126

Хитрость заключается в том, чтобы использовать способности XQuery для работы с общими данными. Сначала я создаю XML для всех столбцов с SELECT *, а затем использую .query(N'data(/x/*)'), который вернет список всех значений, разделенных пробелом.

С последним SELECT я использую EXISTS, чтобы проверить, есть ли хотя бы одно попадание в таблицу @SearchTerms.

Окончательный результат (ища Spar и 78 ) вернет все строки "Sparky" и "Painter 2" из-за номера мобильного телефона с "78"

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