我有两个表:
表1:100行
表2:1000万行
示例:
表1:tb100
create table tb100
(
name varchar(50)
);
insert into tb100 values('Mak John'),('Will Smith'),('Luke W')......100 rows.
表2:tb10mil
create table tb10mil
(
name varchar(50)
);
insert into tb10mil values('John A Mak'),('K Smith Will'),('James Henry')......10 millions rows.
create nonclustered index nci_tb10mil_name on tb10mil(name);
注意:如果另一个表中存在任何 WORD (John,Smith,Will),我想匹配两个表之间的名称。例如John
中出现的John A Mark
。
我的尝试:
首先,我创建了用户定义函数,用于将name
的{{1}}分成几行。
功能:tb100
udf_Split
然后我写了以下查询:
CREATE FUNCTION [dbo].[udf_Split]
(
@InputString VARCHAR(8000),
@Delimiter VARCHAR(50)
)
RETURNS @Items TABLE (ID INTEGER IDENTITY(1,1), Item VARCHAR(8000))
AS
BEGIN
IF @Delimiter = ' '
BEGIN
SET @Delimiter = ','
SET @InputString = REPLACE(@InputString, ' ', @Delimiter)
END
IF (@Delimiter IS NULL OR @Delimiter = '')
SET @Delimiter = ','
DECLARE @Item VARCHAR(8000)
DECLARE @ItemList VARCHAR(8000)
DECLARE @DelimIndex INT
SET @ItemList = @InputString
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
WHILE (@DelimIndex != 0)
BEGIN
SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
INSERT INTO @Items VALUES (@Item)
SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
END
IF @Item IS NOT NULL
BEGIN
SET @Item = @ItemList
INSERT INTO @Items VALUES (@Item)
END
ELSE INSERT INTO @Items VALUES (@InputString)
RETURN
END
以上查询需要20余分钟才能执行。
答案 0 :(得分:4)
我们来谈谈性能
第一点是:尽量避免 标量函数,并尽可能避免使用 multi-statement-TVF 。唯一的 快速方法是 inline-TVF (一种单行声明)。
第二点:尽可能避免循环!
第三点(实际上是第一点):尝试将数据存储在 为快速查询优化的格式。储存多个价值 一个单元格内的细胞违反了1NF和巨大的速度杀手。
您可以尝试以下方法:
模拟表格
CREATE TABLE #t100(ID INT IDENTITY,SomeName VARCHAR(200));
CREATE TABLE #t1M (ID INT IDENTITY,SomeName VARCHAR(200));
INSERT INTO #t100 VALUES('james smith'),('mak john'),('Luke W');
GO
INSERT INTO #t1M values('John A Mak'),('K Smith Will'),('James Henry'),('James John'),('Some other');
GO
-创建表以清晰地存储名称片段(这实际上是您应该使用的格式)
CREATE TABLE #t100Splitted(ID INT IDENTITY PRIMARY KEY,ID_t100 INT,Fragment NVARCHAR(200));
--Use an inline XML-splitter
INSERT INTO #t100Splitted(ID_t100,Fragment)
SELECT ID
,B.frg.value('text()[1]','nvarchar(200)')
FROM #t100
CROSS APPLY(SELECT CAST('<x>' + REPLACE((SELECT SomeName AS [*] FOR XML PATH('')),' ','</x><x>') + '</x>' AS XML)) A(CastedToXml)
CROSS APPLY A.CastedToXml.nodes('/x[text()]') B(frg);
--add indexes
CREATE INDEX IX_t100_ID_t100 ON #t100Splitted(ID_t100);
CREATE INDEX IX_t100_Fragment ON #t100Splitted(Fragment);
--The same for the second table
CREATE TABLE #t1MSplitted(ID INT IDENTITY PRIMARY KEY,ID_t1M INT,Fragment NVARCHAR(200));
INSERT INTO #t1MSplitted(ID_t1M,Fragment)
SELECT ID
,B.frg.value('text()[1]','nvarchar(200)')
FROM #t1M
CROSS APPLY(SELECT CAST('<x>' + REPLACE((SELECT SomeName AS [*] FOR XML PATH('')),' ','</x><x>') + '</x>' AS XML)) A(CastedToXml)
CROSS APPLY A.CastedToXml.nodes('/x[text()]') B(frg);
CREATE INDEX IX_tM_ID_t100 ON #t1MSplitted(ID_t1M);
CREATE INDEX IX_tM_Fragment ON #t1MSplitted(Fragment);
GO
- Check the intermediate results
SELECT * FROM #t100Splitted;
SELECT * FROM #t1MSplitted;
GO
-此查询将返回所有带有公共片段的行
-您可以重新加入源表以获取值
-您可以按t2.ID_t100分组以获取较小表的ID(快得多)
SELECT t1.ID_t1M
FROM #t1MSplitted t1
INNER JOIN #t100Splitted t2 ON t1.Fragment=t2.Fragment
GROUP BY t1.ID_t1M
GO
-清理
DROP TABLE #t100;
GO
DROP TABLE #t1M;
GO
DROP TABLE #t100Splitted;
GO
DROP TABLE #t1MSplitted;
GO
在我的系统上,大约2分钟内处理了1个Mio行。
(点击率很高)
只需拆分,即可将数据转换为更好的形状:〜17分钟
最终选择(只是查找): <1分钟
最终选择,但查找小表的ID:几秒钟
(一旦转换了数据,这将是正常速度)
PSK的方法(带内联拆分):约30分钟
答案 1 :(得分:2)
您可以尝试以下操作。
;WITH splitdata
AS (SELECT splitname
FROM (SELECT *,
Cast('<X>' + Replace(F.Name, ' ', '</X><X>') + '</X>' AS XML)
AS xmlfilter
FROM tb100 F)F1
CROSS apply (SELECT fdata.d.value('.', 'varchar(50)') AS splitName
FROM f1.xmlfilter.nodes('X') AS fdata(d)) O)
SELECT DISTINCT t2.NAME
FROM tb10mil t2
INNER JOIN splitdata S
ON T2.NAME LIKE '%' + S.splitname + '%'
答案 2 :(得分:0)
我试图保存内存,从而避免了加入,从而节省了处理内存的时间。
我试图在较小表中的~420k
值的4
记录上模拟您的问题。
方法是为了避免合并,并将问题内存空间从m x n
限制为至少m&n。
select DISTINCT t2.name
from tb10mil t2
where (SELECT TOP(1) 1 FROM #splitdata where CHARINDEX(data,t2.Problem)>0)=1
结果:该方法所用时间减少了一半。
(reduced from ~28 s to ~14s)
CON::仅当其中一个表很小时,这种方法才有用