Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server T-SQL(2008):比较不同的短语(文本字符串)——WHILE循环/查询优化?_Sql Server_String_Sql Server 2008_While Loop_Split - Fatal编程技术网

Sql server T-SQL(2008):比较不同的短语(文本字符串)——WHILE循环/查询优化?

Sql server T-SQL(2008):比较不同的短语(文本字符串)——WHILE循环/查询优化?,sql-server,string,sql-server-2008,while-loop,split,Sql Server,String,Sql Server 2008,While Loop,Split,免责声明:我不是此数据库的管理员,没有写入权限(我无法创建存储过程或视图)。此外,我还停留在SQL Server 2008/compatibility level 100上 Background:我有两个表,它们共享一个公共ID,但每个表中有一列是文本字符串,每个表之间可能会有很大差异,即使它们描述的是相同的内容。由于每个字符串可能共享几个关键字,因此我创建了一个查询,将每个关键字拆分为临时表的一行(每个源一列),并使用DIFFERENCE()以非常模糊的方式确定是否有足够的单词相似,以假设短语

免责声明:我不是此数据库的管理员,没有写入权限(我无法创建存储过程或视图)。此外,我还停留在SQL Server 2008/compatibility level 100上

Background:我有两个表,它们共享一个公共ID,但每个表中有一列是文本字符串,每个表之间可能会有很大差异,即使它们描述的是相同的内容。由于每个字符串可能共享几个关键字,因此我创建了一个查询,将每个关键字拆分为临时表的一行(每个源一列),并使用DIFFERENCE()以非常模糊的方式确定是否有足够的单词相似,以假设短语可能匹配

我正在连接多个其他值的源表和比较表;即使在所有其他条件相同的情况下,我仍然需要手动验证源中的给定行是否确实存在于基于此字符串的比较中(在WHILE循环开始之前,搜索集已经尽可能窄)

问题:我陷入困境的地方是优化它以快速运行(表连接约30k行)。目前,返回所有结果大约需要30分钟。我可以在不到一分钟的时间内一次返回几千个结果,但我真的希望我错过了一些明显的东西,这会降低我的表现。我绝对不是专家,这是我第一次使用WHILE循环进行查询。在我看来,所有的临时表和变量都在起作用,这似乎过于复杂,但也许我只是盯着它看太久了。提前谢谢

查询示例:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
SET NOCOUNT ON

IF OBJECT_ID('tempdb..#Phrases_A') IS NOT NULL
    BEGIN DROP TABLE #Phrases_A END;
IF OBJECT_ID('tempdb..#Phrases_B') IS NOT NULL
    BEGIN DROP TABLE #Phrases_B END;

CREATE TABLE #Phrases_A -- TEST TABLE A
    (
        ID INT IDENTITY(1,1)
        , [PHRASE A] VARCHAR(8000)
    );

CREATE TABLE #Phrases_B -- TEST TABLE B
    (
        ID INT IDENTITY(1,1)
        , [PHRASE B] VARCHAR(8000)
    );

INSERT INTO #Phrases_A ([PHRASE A]) -- TEST DATA TABLE A
VALUES
    ('the quick brown fox jumped over the lazy dog')
    , ('the awkward aardvark ate an ant')
    , ('small phrase')
    , ('abbr. wrds.')
    , ('Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.')
    , ('This is a potentially unique phrase.')
    , ('This is another non-conformist group of words.')
    , ('This phrase is dissimmilar to its counterpart')
    , ('This phrase matches its counterpart exactly')

INSERT INTO #Phrases_B ([PHRASE B])-- TEST DATA TABLE B
VALUES
    ('the assiduous hound caught the lethargic fox')
    , ('the captivated capybara canoodled a cat')
    , ('this is a not-so-small phrase')
    , ('not abbreviated words')
    , ('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.')
    , ('This is a standardized phrase.')
    , ('This is a standardized phrase.')
    , ('Who cleans the CERN supercollider?')
    , ('This phrase matches its counterpart exactly')

-------------------------------

DECLARE @Increment AS INT, @ID_Limit AS INT, @TimeStamp AS DATETIME;

SET @Increment = 1 -- Used to increment loop while value is less than the max value of a given table (see @ID_Limit)
SET @TimeStamp = SYSDATETIME() -- Used to display seconds elapsed for current loop since execution time

IF OBJECT_ID('tempdb..#TextDump') IS NOT NULL
    BEGIN DROP TABLE #TextDump END;
IF OBJECT_ID('tempdb..#TextDumpSRC') IS NOT NULL
    BEGIN DROP TABLE #TextDumpSRC END;
IF OBJECT_ID('tempdb..#TextDumpCMP') IS NOT NULL
    BEGIN DROP TABLE #TextDumpCMP END;
IF OBJECT_ID('tempdb..#TextMatch') IS NOT NULL
    BEGIN DROP TABLE #TextMatch END;

CREATE TABLE #TextMatch -- Used to evaluate phrases and return SOUNDEX likeness / liklihood of match (note that this is outside of loop)
    (
        ID INT,
        [Phrase Match Data] VARCHAR(8000),
        [Phrase Match Strength] DECIMAL(5,2)
    );

SET @ID_Limit = (SELECT MAX(a.ID) FROM #Phrases_A a) -- Stops loop from running after all rows from source table are complete

-------------------------------

WHILE @Increment <= @ID_Limit
BEGIN

    -- Table dump for given loop
    IF OBJECT_ID('tempdb..#TextDump') IS NOT NULL
        BEGIN DROP TABLE #TextDump END;
    IF OBJECT_ID('tempdb..#TextDumpSRC') IS NOT NULL
        BEGIN DROP TABLE #TextDumpSRC END;
    IF OBJECT_ID('tempdb..#TextDumpCMP') IS NOT NULL
        BEGIN DROP TABLE #TextDumpCMP END;
    IF OBJECT_ID('tempdb..#TextDumpSRCMerge') IS NOT NULL
        BEGIN DROP TABLE #TextDumpSRCMerge END;
    IF OBJECT_ID('tempdb..#TextDumpCMPMerge') IS NOT NULL
        BEGIN DROP TABLE #TextDumpCMPMerge END;

    -- Recreate tables for loop evaluation
    CREATE TABLE #TextDump (ID INT, SRC VARCHAR(8000), CMP VARCHAR(8000));
    CREATE TABLE #TextDumpSRC (SRC VARCHAR(8000));
    CREATE TABLE #TextDumpCMP (CMP VARCHAR(8000));
    CREATE TABLE #TextDumpSRCMerge (ID INT, SRC VARCHAR(8000));
    CREATE TABLE #TextDumpCMPMerge (ID INT, CMP VARCHAR(8000));

    DECLARE @VerifyPrint NVARCHAR(36), @SplitSRC NVARCHAR(256), @SplitCMP NVARCHAR(256), @MatchData AS VARCHAR(8000), @SRC_Count AS INT, @CMP_Count AS INT;

    INSERT INTO #TextDump (SRC, CMP) -- Parses potential dynamic SQL pitfall characters and common articles out and joins source tables on common value, in this case ID.
        SELECT 
            LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(' ' + a.[PHRASE A], '''', ''), '"',''), ',',''), '.',''), '-',' '), ' a ',' '), ' an ',' '), ' the ',' ')))
            , LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(' ' + b.[PHRASE B], '''', ''), '"',''), ',',''), '.',''), '-',' '), ' a ',' '), ' an ',' '), ' the ',' ')))
        FROM #Phrases_A a
        JOIN #Phrases_B b ON b.ID = a.ID
        WHERE a.ID = @Increment

    -- @Split = Dynamic SQL that populates rows of table #TextDump with individual words from current phrase
    SET @SplitSRC = N'INSERT INTO #TextDumpSRC (SRC) VALUES (''' + REPLACE((SELECT SRC FROM #TextDump), ' ' , '''' + '), (''') + ''')';
    SET @SplitCMP = N'INSERT INTO #TextDumpCMP (CMP) VALUES (''' + REPLACE((SELECT CMP FROM #TextDump), ' ' , '''' + '), (''') + ''')';

    -- This is just a way to view time elapsed for individual loop evaluations as query runs (useful for large tables)
    SET @VerifyPrint = CONVERT(NVARCHAR(6), @Increment) + ' PASS; RUNTIME = ' + CONVERT(VARCHAR(24), ABS(DATEDIFF(SECOND, SYSDATETIME(), @TimeStamp))) + ' SEC'

    -- Populates #TextDump rows with individual words from Phrase_A (source)
    EXECUTE sp_executesql @SplitSRC;

    -- Inserts Phrase_A words into their own table while removing words less than 2 chars long
    INSERT INTO #TextDumpSRCMERGE (ID, SRC)
        SELECT @Increment, src.SRC
        FROM #TextDumpSRC src
        WHERE LEN(src.SRC) > 2; --Can be changed as needed

    -- Populates #TextDump rows with individual words from Phrase_B (comparison)
    EXECUTE sp_executesql @SplitCMP;

    -- Inserts Phrase_B words into their own table while removing words less than 2 chars long
    INSERT INTO #TextDumpCMPMerge (ID, CMP)
        SELECT @Increment, cmp.CMP
        FROM #TextDumpCMP cmp
        WHERE LEN(cmp.CMP) > 2; --Can be changed as needed

    -- JOINS words from Phrase_A (SRC) and Phrase_B (CMP) where there's an above medium SOUNDEX likeness; also gives some text feedback on what was joined and strength of match
    SELECT @MatchData = COALESCE(@MatchData + ' ', '') + s.SRC + ' :: ' + c.CMP + ' = ' + CAST(CAST((SUM(DIFFERENCE(s.SRC, c.CMP))/4.0)*100 AS INT) AS VARCHAR(4)) + '%, '
    FROM #TextDumpSRCMerge s
        JOIN #TextDumpCMPMerge c ON DIFFERENCE(s.SRC, c.CMP) >= 3
    WHERE s.ID = @Increment
    GROUP BY s.ID, s.SRC, c.CMP

    SELECT @SRC_Count = (SELECT COUNT(s.ID) FROM #TextDumpSRCMerge s)

    SELECT @CMP_Count = (SELECT COUNT(c.ID) FROM #TextDumpCMPMerge c)

    -- Adds results from this loop iteration to master table, which will ultimately be selected outside the loop
    INSERT INTO #TextMatch (ID, [Phrase Match Data], [Phrase Match Strength])
        SELECT 
            s.ID 
            , @MatchData
            , COALESCE(SUM(DIFFERENCE(s.SRC, c.CMP)/((@SRC_Count + @CMP_Count)*2.0)), 0)
        FROM #TextDumpSRCMerge s
        INNER JOIN #TextDumpCMPMerge c 
            ON DIFFERENCE(s.SRC, c.CMP) >= 3
        WHERE s.ID = @Increment
        GROUP BY s.ID;

    -- Prints the loop time elapsed to Messages tab
    RAISERROR (@VerifyPrint, 1, 1)

    -- Updates the increment value
    SET @Increment = @Increment + 1

    -- Resets the @MatchData variable, though this is likely unnecessary
    SET @MatchData = ''

END;

-------------------------------

SELECT * 
FROM #Phrases_A a
JOIN #Phrases_B b ON b.ID = a.ID 
JOIN #TextMatch m ON m.ID = a.ID
设置事务隔离级别读取未提交
不计较
如果对象_ID('tempdb..#短语_A')不为空
开始放桌#短语#结束;
如果对象_ID('tempdb..#短语_B')不为空
开始放桌#短语#结束;
创建表A——测试表A
(
ID整数标识(1,1)
,[短语A]瓦查尔(8000)
);
创建表#短语_B--测试表B
(
ID整数标识(1,1)
,[短语B]瓦查尔(8000)
);
在测试数据表A中插入短语A([短语A])
价值观
(‘敏捷的棕色狐狸跳过了懒狗’)
,(“笨拙的土豚吃了蚂蚁”)
,(“小短语”)
,(“缩写为wrds.”)
,(“我们的最低成本,我们的最低成本,我们的最低成本,我们的最低成本,我们的最低成本。”
,(“这是一个潜在的独特短语。”)
,(“这是另一组不合规则的词。”)
,(“这个短语与其对应词不同”)
,(“该短语与其对应词完全匹配”)
插入#词组([词组B])——测试数据表B
价值观
(“勤奋的猎犬抓住了昏昏欲睡的狐狸”)
,(“被迷住的水豚捕食了一只猫”)
,(“这不是一个小短语”)
,(“非缩写词”)
,(《劳动和生活的暂时性影响》一书)
,(“这是一个标准化的短语”。)
,(“这是一个标准化的短语”。)
(“谁清洁欧洲核子研究所超级对撞机?”)
,(“该短语与其对应词完全匹配”)
-------------------------------
声明@Increment为INT,@ID\u Limit为INT,@TimeStamp为DATETIME;
设置@Increment=1——当值小于给定表的最大值时,用于递增循环(请参见@ID\u Limit)
SET@TimeStamp=SYSDATETIME()--用于显示当前循环自执行时间起经过的秒数
如果对象_ID('tempdb..#TextDump')不为空
开始放置表格#文本转储结束;
如果对象_ID('tempdb..#TextDumpSRC')不为空
开始放置表格#TextDumpSRC结束;
如果对象_ID('tempdb..#TextDumpCMP')不为空
开始放置表格#文本转储CMP结束;
如果对象_ID('tempdb..#TextMatch')不为空
开始放置表格#文本匹配结束;
创建表#TextMatch——用于评估短语并返回匹配的SOUNDEX相似性/相似性(注意,这在循环之外)
(
ID INT,
[短语匹配数据]VARCHAR(8000),
[短语匹配强度]十进制(5,2)
);
设置@ID_Limit=(从#短语中选择MAX(a.ID)——在源表中的所有行完成后停止循环运行
-------------------------------
而@增量2--可以根据需要进行更改
--用短语_B中的单个单词填充#TextDump行(比较)
执行sp_executesql@SplitCMP;
--将短语_B单词插入其自己的表中,同时删除长度小于2个字符的单词
插入到#TextDumpCMPMerge(ID,CMP)中
选择@Increment,cmp.cmp
从#文本转储cmp
其中LEN(cmp.cmp)>2--可以根据需要进行更改
--连接短语_A(SRC)和短语_B(CMP)中的单词,其中声音相似性在中等以上;还提供了一些关于加入内容和比赛强度的文字反馈
选择@MatchData=COALESCE(@MatchData++,”)+s.SRC++::“+c.CMP+=”+CAST(CAST((求和(差分(s.SRC,c.CMP))/4.0)*100作为INT)作为VARCHAR(4))+'%,”
从#文本转储
连接#TextDumpCMPMerge c ON DIFFERENCE(s.SRC,c.CMP)>=3
其中s.ID=@Increment
按s.ID、s.SRC、c.CMP分组
选择@SRC_Count=(从#textdumpsrc merge中选择Count(s.ID)
选择@CMP_Count=(从#textdumpcmerge c中选择Count(c.ID)
--将此循环迭代的结果添加到主表,主表最终将在循环外部选择
插入到#TextMatch(ID、[短语匹配数据]、[短语匹配强度])
挑选
s、 身份证
,@MatchData
合并(和(差(s.SRC,c.CMP)/(@SRC_计数+@CMP_计数)*2.0)),0)
从#文本转储
内部联接#textdumpcmerge c
差分(s.SRC,c.CMP)>=3
s.ID在哪里=
;with a as(
select
    a.*
    ,aSplit.*
from #Phrases_A a
cross apply 
    dbo.DelimitedSplit8K([Phrase A], ' ') aSplit),

b as(
select
    b.*
    ,bSplit.*
from #Phrases_B b
cross apply 
    dbo.DelimitedSplit8K([Phrase B], ' ') bSplit)

select distinct
    a.ID
    ,a.[PHRASE A]
    ,b.[PHRASE B]
    ,PercentMatched = cast(count(case when a.Item = b.Item then 1 end) / (count(distinct a.Item) * 1.0) * 100 as decimal(5,2))
from a
left join b on b.ID = a.ID 
group by
    a.ID
    ,a.[PHRASE A]
    ,b.[PHRASE B]