Отказ от ответственности : я не являюсь администратором этой базы данных и не имею разрешения на запись (я не могу создавать хранимые процедуры или представления).Кроме того, я застрял на SQL Server 2008 / уровень совместимости 100.
Фон : у меня есть две таблицы, которые имеют общий идентификатор, но один столбец в каждой таблице является текстовой строкой иможет сильно различаться между таблицами, даже если они описывают одно и то же.Поскольку вполне вероятно, что несколько ключевых слов являются общими для каждой строки, я создал запрос, чтобы разбить каждое слово на строку временной таблицы (один столбец для каждого источника) и использовать DIFFERENCE () для определения очень нечеткоспособ, является ли достаточным количество слов, чтобы предположить, что фразы являются вероятным совпадением.
Я присоединяюсь к исходным таблицам и таблицам сравнения по нескольким другим значениям;даже при прочих равных условиях мне все еще нужно вручную проверить, действительно ли данная строка из источника действительно существует в сравнении, основанном на этой строке (перед началом цикла WHILE набор поиска уже настолько узок, насколько это возможно).
Проблема : Там, где я застреваю, я оптимизирую это для быстрого запуска (таблицы объединяются на ~ 30 тыс. Строк).В настоящее время для возврата всех результатов требуется около 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