Настройка производительности на соответствие слова с использованием CHARINDEX и COLLATION - PullRequest
0 голосов
/ 30 января 2019

У меня есть две таблицы со следующими записями:

Таблица 1: 100 строк

Таблица 2: 50 миллионов строк

Пример:

Таблица 1: tb100

create table tb100
(
    name varchar(50)
);

insert into tb100 values('Mak John'),('Will Smith'),('Luke W')......100 rows.

Таблица 2: tb50mil

create table tb50mil
(
    name varchar(50)
);

insert into tb10mil values('John A Mak'),('K Smith Will'),('James Henry')......50 millions rows.    

create nonclustered index nci_tb10mil_name  on tb10mil(name);

Требование: я хочу сопоставить имя между двумя таблицами, если оно есть WORD (Джон, Смит, Уилл) присутствуют в другой таблице.Например, John присутствует в John A Mark.

Моя попытка: Использовал XML для разделения столбца name таблицы tb100, а также добавления collation с CHARINDEX.

;WITH splitdata AS 
(
    SELECT splitname 
    FROM   
    (
        SELECT *,Cast('<X>' + Replace(t.name, ' ', '</X><X>') + '</X>' AS XML) AS  xmlfilter 
        FROM   tb100 t
    )F1 
    CROSS apply 
    (
        SELECT fdata.d.value('.', 'varchar(50)') AS splitName 
        FROM   f1.xmlfilter.nodes('X') AS fdata(d)
    ) O
) 
SELECT t2.name AS [Aadhar Names]
FROM   tb50mil t2 
INNER JOIN splitdata S 
ON CHARINDEX(S.splitname collate Latin1_General_BIN,T2.name collate Latin1_General_BIN)>0
GROUP BY t2.name

Время, необходимое для выполнения: 00: 01: 34
Затронутые строки: (2251429 затронутых строк)

План выполнения:

enter image description here

1 Ответ

0 голосов
/ 30 января 2019

Если вам нужны отдельные слова в вашем имени, то, возможно, наличие таблицы, в которой ваше имя - только одна строка, не является оптимальным с точки зрения концепции.Кроме того, разделение имен теперь является болезненным, поскольку у вас нет повторяющихся шаблонов для ваших отчеств.Плюс модификация строки на самом деле не является сильной стороной SQL.Вместо этого я бы расширил ваш стол примерно так:

alter table tb100 
add 
nameID int IDENTITY(1,1) NOT NULL,
first_name varchar(50) null,
middle_name varchar(50) null,
last_name varchar(50) null

insert into tb100 values('Mak John'),('Will Smith'),('Luke W')......100 rows.

if (SELECT LEN(col) - LEN(REPLACE(col, ' ', '')) > 1)
update tb100
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name)))),
last_name = (select Substring( Right(middle_name, (LEN(middle_name) - (Charindex(' ', middle_name) + 1)), LEN(LEN(middle_name) - (Charindex(' ', middle_name) + 1))

else 
update tb100
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = '',
last_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name))))

Надеюсь, это сработает, у меня не было возможности протестировать его, потому что я в дороге.Если у вас есть возможность вставить данные в эти столбцы без всей этой модификации, сделайте это.Затем вы делаете то же самое с вашим другим столом ...

alter table tb50mil 
add 
nameID int IDENTITY(1,1) NOT NULL,
first_name varchar(50) null,
middle_name varchar(50) null,
last_name varchar(50) null

insert into tb10mil values('John A Mak'),('K Smith Will'),('James Henry')......50 million rows.

if (SELECT LEN(col) - LEN(REPLACE(col, ' ', '')) > 1)
update tb50mil
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name)))),
last_name = (select Substring( Right(middle_name, (LEN(middle_name) - (Charindex(' ', middle_name) + 1)), LEN(LEN(middle_name) - (Charindex(' ', middle_name) + 1))

else 
update tb50mil
set
first_name = (Select Substring(name, 0, (Charindex(' ', name)))),
middle_name = '',
last_name = (Select Substring( Right(name, (LEN(name) - (Charindex(' ', name) +1)), 0, (Charindex(' ', name))))

и с этого момента это действительно простое соединение:

select * from tb100 hun
inner join
tb50mil mil on hun.first_name = mil.first_name OR hun.middle_name = mil.middle_name OR hun.last_name OR mil.last_name

Надеюсь, это поможет!

...