请提供更多答案... - 在 SQL 中执行此文本搜索的最有效方法是什么?
More answers please... - What is the most efficient way of doing this text search in SQL?
它的丑陋是出于必要,但它仍然需要 运行 在适当的时候,我将展示我尝试实现它的两种方法。我相信递归 CTE 最终会解决它,但我在实施它时遇到了一些严重的困难。
基本上,我在 db1
中有一个名为 dis_det
的 table,其中有 3 个重要字段:dis_id
、dis_type
和 dis_q_val
. dis_q_val
是要搜索特定术语和短语的文本字段。
搜索条件位于 db2
中的 table 中,名为 tag_keywords
,还有 3 个相关字段:tag_id
、keyword_id
、keyword_name
。 keyword_name
是要搜索的文本列表,tag_id
是带有标记信息的 table 的外键。
我的最终要求是 DISTINCT dis_id, tag_id
的列表。我已经知道我不能只 FREETEXT
整个事情,因为我每个关键字得到 7-25,000 个结果,而使用 LIKE '%' + keyword_name + '%'
returns 最多大约 1500 个结果。这不是一个巨大的测试集(大约 180,000 个披露记录,<1000 个关键字),但是 LIKE
版本 运行 在查询大约 17 小时后内存不足,这对于处理来说太长了。另一方面,使用 FREETEXT
,大约 20 分钟后 运行 内存不足。我可以获得更多内存,但我需要及时获得准确的结果。
所以我的主要问题是 "Will a recursive CTE fix this?",如果是,"How do I implement it?"。我还想在 keyword_name
中集成对多个单词的检查,这样我就可以对多个单词使用 LIKE
,对单个单词值使用 FREETEXT
。
现在输入一些代码:
--query using FREETEXT
DECLARE @i INT = 1, @keyword NVARCHAR(65)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i < 909
BEGIN
SET @keyword = (SELECT keyword_name FROM db2..tag_keywords WHERE keyword_id = @i)
INSERT INTO @results
SELECT DISTINCT dis_id
, (SELECT tag_id FROM db2..tag_keywords WHERE keyword_id = @i)
FROM db1..dis_det
WHERE dis_type = 'reg' AND FREETEXT(dis_val, @keyword)
SET @i = @i + 1
END
--runs for about 45 minutes and runs out of memory with way too many results
.
--query using LIKE
DECLARE @i INT = 1, @keyword NVARCHAR(65)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i < 909
BEGIN
SET @keyword = (SELECT keyword_name FROM db2..tag_keywords WHERE keyword_id = @i)
INSERT INTO @results
SELECT DISTINCT dis_id
, (SELECT tag_id FROM db2..tag_keywords WHERE keyword_id = @i)
FROM db1..dis_det
WHERE dis_type = 'reg' AND dis_val LIKE '%' + @keyword + '%'
SET @i = @i + 1
END
--runs for about 17 hours and runs out of memory
我真的不知道如何操纵递归 CTE 的示例来使它们与此一起工作。如果您有可行的实现,我将不胜感激。
据我所知,递归 CTE 的锚点应该如下所示:
SELECT d.dis_id
, (SELECT tag_id FROM db2..tag_keywords WHERE tag_keyword_id = @j) AS tag_id
, 1 AS @j
FROM db1..dis_det AS d
WHERE d.dis_type = 'reg' AND
(d.dis_q_val LIKE '%' + (SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j) + '%'
AND (SELECT CHARINDEX(' ',(SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j))) > 0
) OR (FREETEXT(d.dis_q_val, (SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j))
AND (SELECT CHARINDEX(' ',(SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j))) = 0
)
但是该查询给出了错误,我不确定如何将 @j 递增写入其中。
再次感谢,抱歉花了这么长时间 post 更多信息。
/*
* -- Latest edit, still looking for a better solution: 02/27/2016 --
*/
我仍在寻找比我的自我回答更快 运行 的解决方案,它只是上述代码的工作集合。根据 FREETEXT INDEX 的状态,它 运行 在 8 到 14 小时之间。在大多数情况下,这种事情几乎是无法接受的table。我希望递归或合并技术能更有效地应对这种情况,但我不确定如何实施。
再次感谢。
在有人提出更好的建议之前,我已经为我的低效解决方案提出了上述理想的混合:
DECLARE @i INT = 1, @tag_id INT, @keyword NVARCHAR(65)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i < 909
BEGIN
SET @tag_id = (SELECT tag_id FROM db2..tag_keywords WHERE keyword_id = @i)
SET @keyword = (SELECT keyword_name FROM db2..tag_keywords WHERE keyword_id = @i)
IF(CHARINDEX(@keyword, ' ') >= 0)
BEGIN
INSERT INTO @results
SELECT DISTINCT dis_id, @tag_id
FROM db1..dis_det
WHERE dis_type = 'reg' AND dis_value LIKE '%' + @keyword + '%'
END
IF(CHARINDEX(@keyword, ' ') <0)
BEGIN
INSERT INTO @results
SELECT DISTINCT dis_id, @tag_id
FROM db1..dis_det
WHERE dis_type = 'reg' AND FREETEXT(dis_value, @keyword)
END
PRINT 'Iteration number: ' + CAST(@i AS NVARCHAR(3)) --to mark progress while watching it run
SET @i = @i + 1
END
SELECT * FROM @results
ORDER BY dis_id, tag_id
运行大约 8.5 小时。对于可能 运行 每月一次的事情来说显然不是最佳选择,但我相信城里的大人物可以为周末类型的交易设置它。
这将是公认的答案,直到有人想出更快的 运行 时间。
感谢您的浏览。
如评论中所述,我发布了关于简单加入 table 来执行查询的想法。
DECLARE @i INT = 1, @max INT = (SELECT MAX(tag_keyword_id) FROM db2..tag_keywords)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i <= @max
BEGIN
SELECT DISTINCT details.dis_id, keyword.tag_id
FROM db2..tag_keywords keyword
JOIN db1..dis_det details
ON details.dis_type='reg' AND details.dis_q_val like '%' + keyword.keyword_name + '%'
GROUP BY details.dis_id, keyword.tag_id
PRINT 'Iteration number: ' + CAST(@i AS NVARCHAR(5)) + 'of: ' + CAST(@max AS NVARCHAR(5))
SET @i = @i + 1
END
SELECT * FROM @results
ORDER BY dis_id, tag_id
另一个出现在我脑海中的想法是某种预处理,我实际上不时这样做。就像将新关键字添加到 db2 时一样,关键字 table 上的某些触发器会 运行 查询并将结果以所需格式存储在某些 table 中,以便您稍后查询需要。这当然取决于条件,但是如果 db1 增长很快并且添加的关键字不那么频繁,它可能会带来显着的性能提升。
它的丑陋是出于必要,但它仍然需要 运行 在适当的时候,我将展示我尝试实现它的两种方法。我相信递归 CTE 最终会解决它,但我在实施它时遇到了一些严重的困难。
基本上,我在 db1
中有一个名为 dis_det
的 table,其中有 3 个重要字段:dis_id
、dis_type
和 dis_q_val
. dis_q_val
是要搜索特定术语和短语的文本字段。
搜索条件位于 db2
中的 table 中,名为 tag_keywords
,还有 3 个相关字段:tag_id
、keyword_id
、keyword_name
。 keyword_name
是要搜索的文本列表,tag_id
是带有标记信息的 table 的外键。
我的最终要求是 DISTINCT dis_id, tag_id
的列表。我已经知道我不能只 FREETEXT
整个事情,因为我每个关键字得到 7-25,000 个结果,而使用 LIKE '%' + keyword_name + '%'
returns 最多大约 1500 个结果。这不是一个巨大的测试集(大约 180,000 个披露记录,<1000 个关键字),但是 LIKE
版本 运行 在查询大约 17 小时后内存不足,这对于处理来说太长了。另一方面,使用 FREETEXT
,大约 20 分钟后 运行 内存不足。我可以获得更多内存,但我需要及时获得准确的结果。
所以我的主要问题是 "Will a recursive CTE fix this?",如果是,"How do I implement it?"。我还想在 keyword_name
中集成对多个单词的检查,这样我就可以对多个单词使用 LIKE
,对单个单词值使用 FREETEXT
。
现在输入一些代码:
--query using FREETEXT
DECLARE @i INT = 1, @keyword NVARCHAR(65)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i < 909
BEGIN
SET @keyword = (SELECT keyword_name FROM db2..tag_keywords WHERE keyword_id = @i)
INSERT INTO @results
SELECT DISTINCT dis_id
, (SELECT tag_id FROM db2..tag_keywords WHERE keyword_id = @i)
FROM db1..dis_det
WHERE dis_type = 'reg' AND FREETEXT(dis_val, @keyword)
SET @i = @i + 1
END
--runs for about 45 minutes and runs out of memory with way too many results
.
--query using LIKE
DECLARE @i INT = 1, @keyword NVARCHAR(65)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i < 909
BEGIN
SET @keyword = (SELECT keyword_name FROM db2..tag_keywords WHERE keyword_id = @i)
INSERT INTO @results
SELECT DISTINCT dis_id
, (SELECT tag_id FROM db2..tag_keywords WHERE keyword_id = @i)
FROM db1..dis_det
WHERE dis_type = 'reg' AND dis_val LIKE '%' + @keyword + '%'
SET @i = @i + 1
END
--runs for about 17 hours and runs out of memory
我真的不知道如何操纵递归 CTE 的示例来使它们与此一起工作。如果您有可行的实现,我将不胜感激。
据我所知,递归 CTE 的锚点应该如下所示:
SELECT d.dis_id
, (SELECT tag_id FROM db2..tag_keywords WHERE tag_keyword_id = @j) AS tag_id
, 1 AS @j
FROM db1..dis_det AS d
WHERE d.dis_type = 'reg' AND
(d.dis_q_val LIKE '%' + (SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j) + '%'
AND (SELECT CHARINDEX(' ',(SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j))) > 0
) OR (FREETEXT(d.dis_q_val, (SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j))
AND (SELECT CHARINDEX(' ',(SELECT keyword_name FROM db2.tag_keywords WHERE tag_keyword_id = @j))) = 0
)
但是该查询给出了错误,我不确定如何将 @j 递增写入其中。
再次感谢,抱歉花了这么长时间 post 更多信息。
/*
* -- Latest edit, still looking for a better solution: 02/27/2016 --
*/
我仍在寻找比我的自我回答更快 运行 的解决方案,它只是上述代码的工作集合。根据 FREETEXT INDEX 的状态,它 运行 在 8 到 14 小时之间。在大多数情况下,这种事情几乎是无法接受的table。我希望递归或合并技术能更有效地应对这种情况,但我不确定如何实施。
再次感谢。
在有人提出更好的建议之前,我已经为我的低效解决方案提出了上述理想的混合:
DECLARE @i INT = 1, @tag_id INT, @keyword NVARCHAR(65)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i < 909
BEGIN
SET @tag_id = (SELECT tag_id FROM db2..tag_keywords WHERE keyword_id = @i)
SET @keyword = (SELECT keyword_name FROM db2..tag_keywords WHERE keyword_id = @i)
IF(CHARINDEX(@keyword, ' ') >= 0)
BEGIN
INSERT INTO @results
SELECT DISTINCT dis_id, @tag_id
FROM db1..dis_det
WHERE dis_type = 'reg' AND dis_value LIKE '%' + @keyword + '%'
END
IF(CHARINDEX(@keyword, ' ') <0)
BEGIN
INSERT INTO @results
SELECT DISTINCT dis_id, @tag_id
FROM db1..dis_det
WHERE dis_type = 'reg' AND FREETEXT(dis_value, @keyword)
END
PRINT 'Iteration number: ' + CAST(@i AS NVARCHAR(3)) --to mark progress while watching it run
SET @i = @i + 1
END
SELECT * FROM @results
ORDER BY dis_id, tag_id
运行大约 8.5 小时。对于可能 运行 每月一次的事情来说显然不是最佳选择,但我相信城里的大人物可以为周末类型的交易设置它。
这将是公认的答案,直到有人想出更快的 运行 时间。
感谢您的浏览。
如评论中所述,我发布了关于简单加入 table 来执行查询的想法。
DECLARE @i INT = 1, @max INT = (SELECT MAX(tag_keyword_id) FROM db2..tag_keywords)
DECLARE @results TABLE(dis_id BIGINT, tag_id INT)
WHILE @i <= @max
BEGIN
SELECT DISTINCT details.dis_id, keyword.tag_id
FROM db2..tag_keywords keyword
JOIN db1..dis_det details
ON details.dis_type='reg' AND details.dis_q_val like '%' + keyword.keyword_name + '%'
GROUP BY details.dis_id, keyword.tag_id
PRINT 'Iteration number: ' + CAST(@i AS NVARCHAR(5)) + 'of: ' + CAST(@max AS NVARCHAR(5))
SET @i = @i + 1
END
SELECT * FROM @results
ORDER BY dis_id, tag_id
另一个出现在我脑海中的想法是某种预处理,我实际上不时这样做。就像将新关键字添加到 db2 时一样,关键字 table 上的某些触发器会 运行 查询并将结果以所需格式存储在某些 table 中,以便您稍后查询需要。这当然取决于条件,但是如果 db1 增长很快并且添加的关键字不那么频繁,它可能会带来显着的性能提升。