【问题标题】:TSQL matching as many comma-separated tags as possibleSQL 匹配尽可能多的逗号分隔标签
【发布时间】:2016-06-24 13:05:41
【问题描述】:

一个表包含一个Title 字段和一个Tags 字段。标签是通过文档中的潜在狄利克雷分配 (LDA) 生成的,可以是例如'鱼、烤箱、时间'、'BBQ、啤酒'或'肉、BBQ'。标签的长度不固定。

给定一组标签,无论标签顺序如何,如何找到匹配标签数量最多的记录?

所以,如果给出'BBQ,肉',最好的结果应该是'肉,BBQ'。如果给出 'BBQ, fish, cream' 则可以返回所有三个记录(它们都有一个匹配的标签)。

【问题讨论】:

  • 将单词拆分到单独的表格中,每行一个,并将 FK 返回到文档
  • “鱼”在“一条鱼,两条鱼,红鱼,蓝鱼”中匹配多少次?这算一还是五? (四,先生!)

标签: sql-server tsql


【解决方案1】:

使用this function 并创建这个

    CREATE FUNCTION dbo.getCountOfMatch ( @mainString VARCHAR(MAX),  @searchString nvarchar(max))
    RETURNS
      INT
    AS
    BEGIN
        DECLARE @returnCount INT

        SELECT 
            @returnCount = COUNT(1)
        FROM 
            splitstring(@mainString) A INNER JOIN 
            splitstring(@searchString) B ON A.Name = B.Name

        RETURN @returnCount
    END

    SELECT TOP 1 // What you want
      Title,
      Tags
    FROM
    (
        SELECT
            A.Title,
            A.Tags,
            dbo.getCountOfMatch(A.Tags, @search) CountTags -- The number of matches.
        FROM
            TABLE A
    ) B
    ORDER BY B.CountTags DESC

更新

DECLARE @searchText NVARCHAR(MAX) = 'BBQ, meat'
DECLARE @query NVARCHAR(MAX) = '
        SELECT
            *
        FROM
            Table
        WHERE '

SELECT
    @query += 
    (
        SELECT
            'Tags like ''%' + A.Name + '%'' AND ' -- Dont forget trim!
        FROM 
            splitstring(@searchText) A
        FOR XML PATH ('')
    )

SELECT @query = LEFT(@query, LEN(@query) - 4) + 'ORDER BY LEN(Tags)' -- For exactly matching: LEN(Tags) = LEN(@searchText)

EXEC sp_executesql  @query

查询的样子;

    SELECT
        *
    FROM
        Table
    WHERE 
        Tags like '%BBQ%' AND 
        Tags like '%meat%'
    ORDER BY LEN(Tags) 

【讨论】:

  • 效果很好,但速度非常慢,即使在小样本上也是如此(这里 14K 行需要 17 秒)。
【解决方案2】:

结合两个 UDF,您可以返回搜索的命中率(百分比)。

例如

Select [dbo].[udf-Str-Match-Rate]('Dog,House,Custom',',','The dog house is red',' ')

返回 0.6666 -- 找到 3 个单词/短语中的 2 个。

每个都可以有自己的分隔符

只测试不同的词以避免夸大结果

我还包括了一个 soundex(这是可选的)

第一个UDF是独立的,可以独立使用。

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@delimeter varchar(10))
--Usage: Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--       Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--       Select * from [dbo].[udf-Str-Parse]('id26,id46|id658,id967','|')

Returns @ReturnTable Table (Key_PS int IDENTITY(1,1) NOT NULL , Key_Value varchar(max))

As

Begin
   Declare @intPos int,@SubStr varchar(max)
   Set @IntPos = CharIndex(@delimeter, @String)
   Set @String = Replace(@String,@delimeter+@delimeter,@delimeter)
   While @IntPos > 0
      Begin
         Set @SubStr = Substring(@String, 0, @IntPos)
         Insert into @ReturnTable (Key_Value) values (@SubStr)
         Set @String = Replace(@String, @SubStr + @delimeter, '')
         Set @IntPos = CharIndex(@delimeter, @String)
      End
   Insert into @ReturnTable (Key_Value) values (@String)
   Return 
End

第二个 UDF 需要第一个

CREATE FUNCTION [dbo].[udf-Str-Match-Rate] (@SearchFor varchar(max),@SearchForDelim varchar(5),@SearchIn varchar(max),@SearchInDelim varchar(5))

-- Syntax : Select [dbo].[udf-Str-Match-Rate]('Dog,House,Custom',',','The dog house is red',' ')

Returns money
AS
  BEGIN

    Declare @RetVal money

    ;with cteSearchFor  as (Select Distinct Key_Value from [dbo].[udf-Str-Parse](@SearchFor ,@SearchForDelim))
         ,cteSearchIn as (Select Distinct Key_Value from [dbo].[udf-Str-Parse](@SearchIn,@SearchInDelim))
         ,cteWordCnt as (Select Words=cast(count(*) as money) From cteSearchFor)
    Select @RetVal = isnull(Count(*)/max(Words),0)
      From cteSearchFor S 
      Join cteWordCnt W on 1=1
      Join cteSearchIn C
        on S.Key_Value = C.Key_Value
        or Soundex(S.Key_Value) = Soundex(C.Key_Value)

    Return @RetVal

  END

【讨论】:

    【解决方案3】:

    创建一个表标签并使用stringsplit 进行填充

    tags
    title PK
    tag   PK  
    
    select title, count(*) 
    from tags 
    where tag in ('BBQ', 'fish', 'cream') 
    group by title 
    having count(*) > 1
    
    SELECT * 
    from table 
    join dbo.splitstring(table.tags)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-30
      相关资源
      最近更新 更多