te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>t sql - How can I perform an 'AND' search across multiple full text index tables in my SQL Server command? - Sta
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

t sql - How can I perform an 'AND' search across multiple full text index tables in my SQL Server command? - Sta

programmeradmin4浏览0评论

I'm currently using SQL Server 2016 and I need to search for n-number of user input terms across four different full-text index tables. We originally tried this:

DECLARE @searchString varchar(1000)
DECLARE @searchText varchar(1000)

SET @searchText = 'black schwinn bike'  --input by user

SET @searchString = 'FORMSOF(INFLECTIONAL, "' + RTRIM(LTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@searchText,' AND ',','), ' OR ',','),' & ',','),' ',','),',','") OR FORMSOF(INFLECTIONAL,"'))) + '")'                                                 
--'FORMSOF(INFLECTIONAL, "black") OR FORMSOF(INFLECTIONAL,"schwinn") OR FORMSOF(INFLECTIONAL,"bike")'

SELECT DISTINCT 
    COALESCE(ftProduct.ProductKey,ftBrand.BrandKey, ftOption.OptionKey) AS KeyFound, 
    COALESCE(ftProduct.ProductRank, ftBrand.BrandRank, ftOption.OptionRank) AS Rank
FROM 
    (
        SELECT 
            [KEY] AS ProductKey, 
            [RANK] AS ProductRank
        FROM CONTAINSTABLE(tbProducts,*, @searchString )                    
     ) AS ftProduct
FULL OUTER JOIN
    (
        SELECT 
            [KEY] as BrandKey,
            [RANK] AS BrandRank
        FROM CONTAINSTABLE(tbBrands,*, @searchString)       
     ) AS ftBrand
     ON  ftProduct.ProductKey=ftBrand.BrandKey
FULL OUTER JOIN
    (
        SELECT 
            [KEY] AS OptionKey, 
            [RANK] AS OptionRank
        FROM CONTAINSTABLE(tbOptions,*, @searchString)      
     ) AS ftOption
     ON  ftProduct.ProductKey=ftOPtion.OptionKey

This worked when the user wanted to do an OR search but we need it to do an AND search. The All-inclusive search has turned out to be much more difficult. If I change the OR to AND when I build the @searchString this will only find records where all 3 terms are found in the same table. If I keep @searchString as OR but add an AND condition to the main SELECT, then it's possible it's finding the 3 terms in all of the tables but it may also be finding the same term in multiple tables. CONTAINSTABLE only returns Key and Rank, it does not tell me which of the 3 terms was found in the table. So, I have no way of knowing if all 3 terms were found or if just one or two of the terms was found multiple times in different tables.

Finally, I realized that the only way to be sure I am finding at least one term across all 3 tables is to search each term separately. I used a CTE to build a table of the provided search terms, then joined that to the result of CONTAINSTABLE. This is a simplified example of what I tried.

DECLARE @searchText varchar(1000)    
SET @searchText = 'black schwinn bike'

DECLARE @searchTerms TABLE (Term varchar(100))
INSERT INTO @searchTerms (Term)
SELECT value FROM STRING_SPLIT(@searchText, ' ')

;WITH searchConditions AS (
    SELECT 
        Term AS searchTerm,
        'FORMSOF(INFLECTIONAL, "' + Term + '")' AS searchCondition
    FROM @searchTerms
) 
    SELECT 
        [KEY] AS ProductKey,
        [RANK] AS ProductRank
    FROM searchConditions AS sc
        CROSS APPLY CONTAINSTABLE(tblRL_Products, *, sc.searchCondition )        
        --CROSS APPLY CONTAINSTABLE(tblRL_Products, *, 'FORMSOF(INFLECTIONAL, "black")' )  

This is throwing a syntax error because it does not want me passing in the sc.searchCondition into the CONTAINSTABLE function. It will only take a straight string value and not a table referenced value. If you uncomment out the CROSS APPLY beneath it to swap those lines it runs fine. I believe this is a limitation of CONTAINSTABLE.

I can't seem to find a solution to search for multiple terms across multiple full-text index tables while making sure each term appears in at least one of the tables. I'm hoping someone can help me with a different solution or a workaround for the issue with CONTAINSTABLE not accepting a value from the JOINED table.

I'm currently using SQL Server 2016 and I need to search for n-number of user input terms across four different full-text index tables. We originally tried this:

DECLARE @searchString varchar(1000)
DECLARE @searchText varchar(1000)

SET @searchText = 'black schwinn bike'  --input by user

SET @searchString = 'FORMSOF(INFLECTIONAL, "' + RTRIM(LTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@searchText,' AND ',','), ' OR ',','),' & ',','),' ',','),',','") OR FORMSOF(INFLECTIONAL,"'))) + '")'                                                 
--'FORMSOF(INFLECTIONAL, "black") OR FORMSOF(INFLECTIONAL,"schwinn") OR FORMSOF(INFLECTIONAL,"bike")'

SELECT DISTINCT 
    COALESCE(ftProduct.ProductKey,ftBrand.BrandKey, ftOption.OptionKey) AS KeyFound, 
    COALESCE(ftProduct.ProductRank, ftBrand.BrandRank, ftOption.OptionRank) AS Rank
FROM 
    (
        SELECT 
            [KEY] AS ProductKey, 
            [RANK] AS ProductRank
        FROM CONTAINSTABLE(tbProducts,*, @searchString )                    
     ) AS ftProduct
FULL OUTER JOIN
    (
        SELECT 
            [KEY] as BrandKey,
            [RANK] AS BrandRank
        FROM CONTAINSTABLE(tbBrands,*, @searchString)       
     ) AS ftBrand
     ON  ftProduct.ProductKey=ftBrand.BrandKey
FULL OUTER JOIN
    (
        SELECT 
            [KEY] AS OptionKey, 
            [RANK] AS OptionRank
        FROM CONTAINSTABLE(tbOptions,*, @searchString)      
     ) AS ftOption
     ON  ftProduct.ProductKey=ftOPtion.OptionKey

This worked when the user wanted to do an OR search but we need it to do an AND search. The All-inclusive search has turned out to be much more difficult. If I change the OR to AND when I build the @searchString this will only find records where all 3 terms are found in the same table. If I keep @searchString as OR but add an AND condition to the main SELECT, then it's possible it's finding the 3 terms in all of the tables but it may also be finding the same term in multiple tables. CONTAINSTABLE only returns Key and Rank, it does not tell me which of the 3 terms was found in the table. So, I have no way of knowing if all 3 terms were found or if just one or two of the terms was found multiple times in different tables.

Finally, I realized that the only way to be sure I am finding at least one term across all 3 tables is to search each term separately. I used a CTE to build a table of the provided search terms, then joined that to the result of CONTAINSTABLE. This is a simplified example of what I tried.

DECLARE @searchText varchar(1000)    
SET @searchText = 'black schwinn bike'

DECLARE @searchTerms TABLE (Term varchar(100))
INSERT INTO @searchTerms (Term)
SELECT value FROM STRING_SPLIT(@searchText, ' ')

;WITH searchConditions AS (
    SELECT 
        Term AS searchTerm,
        'FORMSOF(INFLECTIONAL, "' + Term + '")' AS searchCondition
    FROM @searchTerms
) 
    SELECT 
        [KEY] AS ProductKey,
        [RANK] AS ProductRank
    FROM searchConditions AS sc
        CROSS APPLY CONTAINSTABLE(tblRL_Products, *, sc.searchCondition )        
        --CROSS APPLY CONTAINSTABLE(tblRL_Products, *, 'FORMSOF(INFLECTIONAL, "black")' )  

This is throwing a syntax error because it does not want me passing in the sc.searchCondition into the CONTAINSTABLE function. It will only take a straight string value and not a table referenced value. If you uncomment out the CROSS APPLY beneath it to swap those lines it runs fine. I believe this is a limitation of CONTAINSTABLE.

I can't seem to find a solution to search for multiple terms across multiple full-text index tables while making sure each term appears in at least one of the tables. I'm hoping someone can help me with a different solution or a workaround for the issue with CONTAINSTABLE not accepting a value from the JOINED table.

Share Improve this question asked Feb 17 at 19:09 LarryGLarryG 6971 gold badge7 silver badges17 bronze badges 1
  • I also tried this solution (stackoverflow/questions/40378070/…). However, I need a LEFT JOIN and SQL will now allow me to index a view with an OUTER JOIN, with a CTE, a derived table, or a view referencing another view. It seems impossible for me to get a full text index on a view that has to have a left join in it. – LarryG Commented 2 days ago
Add a comment  | 

1 Answer 1

Reset to default 1

You could use dynamic SQL for this. The following script will do this for you.

We union all the results together, then group by KEY and only include grouped results where the number of unique searchTerm values is equal to the number of search terms we started with.

DECLARE @searchText nvarchar(1000) = 'black schwinn bike';

DECLARE @sql nvarchar(max);

DECLARE @searchTerms TABLE (Term varchar(100))
INSERT INTO @searchTerms (Term)
SELECT value FROM STRING_SPLIT(@searchText, ' ');

SET @sql = N'
WITH searches AS (' + (

SELECT STRING_AGG(CONVERT(nvarchar(max), N'
    SELECT
        ' + QUOTENAME(Term, '''') + N' AS searchTerm,
        [KEY],
        RANK
    FROM CONTAINSTABLE(tblRL_Products, *, ' + QUOTENAME('FORMSOF(INFLECTIONAL, ' + QUOTENAME(Term, '"') + ')', '''') + ' )
    UNION ALL
    SELECT
        ' + QUOTENAME(Term, '''') + N' AS searchTerm,
        [KEY],
        RANK
    FROM CONTAINSTABLE(tbBrands, *, ' + QUOTENAME('FORMSOF(INFLECTIONAL, ' + QUOTENAME(Term, '"') + ')', '''') + ' )
    UNION ALL
    SELECT
        ' + QUOTENAME(Term, '''') + N' AS searchTerm,
        [KEY],
        RANK
    FROM CONTAINSTABLE(tbOptions, *, ' + QUOTENAME('FORMSOF(INFLECTIONAL, ' + QUOTENAME(Term, '"') + ')', '''') + ' )
'), '
    UNION ALL
')
FROM @searchTerms

) + N'
)
SELECT
  [KEY] AS KeyFound,
  AVG(1.0 * RANK) AS Rank
FROM searches
GROUP BY
  [KEY]
HAVING COUNT(DISTINCT searchTerm) = ' + CONVERT(nvarchar(30), (SELECT COUNT(*) FROM @searchTerms));

PRINT @sql;   -- your friend

EXEC sp_executesql @sql;     -- add parameters if necessary

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论