搜索查询 - 搜索多个表和列

时间:2018-05-16 16:13:48

标签: sql-server tsql

我有一个数据库,我在一个自由文本搜索中返回有关几个实体的信息,这是一个示例数据库:

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 != ''

接下来,我遍历我的搜索术语,将每个实体插入我的Temp表:

            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不包含搜索词的任何内容。

我在删除之前从临时表中选择了所有内容。

虽然这确实有效,并且运行良好,但搜索速度比我想要的慢,而且我正在寻找加快速度的建议。其中之一是创建索引表并将所有实体转储到此中,并且只有1个循环获取特定搜索。这稍微快一点,但这也意味着我只有最后一个任务被设置为将数据转储到索引中的数据。实时搜索势在必行。

感谢您的任何建议。

2 个答案:

答案 0 :(得分:0)

首先是一些观察结果:

  • 如果您的表格结构相同(如此处所示),最好不要保留不同的表格,而是将它们全部保存在一个中,并附加一列来设置“数据类型”。

  • 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的中间结果就是这个(仅列IdConcatenated):

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的能力来处理通用数据。首先,我使用SELECT *创建所有列的XML,然后使用.query(N'data(/x/*)'),它将返回由空格分隔的所有值的列表。

最后SELECT我使用EXISTS来检查表格@SearchTerms中是否至少有一次点击。

最终结果(寻找 Spar 78 )将返回所有“Sparky”行和“Painter 2”,因为手机号码为“78”

答案 1 :(得分:0)

我不确定这是否会更快,但您是否尝试创建一个字符串并在该字符串上使用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”这样的搜索词匹配“Jar Jar”“Edwards”等的FirstName + LastName。