如何提高 nvarchar(max) 列的join效率

肥龙上天 2013-11-12 01:50:27

IF OBJECT_ID (N'StormyWeather', N'U') IS NOT NULL
DROP TABLE StormyWeather
GO
CREATE TABLE StormyWeather (
StormID INT NOT NULL IDENTITY,
StormHead NVARCHAR(50) NOT NULL,
StormBody NVARCHAR(MAX) NOT NULL,
)
GO

INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Stormy Weather Delays Travel',
'The stormy weather made travel by motor vehicle difficult.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Stormier Weather on Monday',
'The stormier weather on Monday made vehicle travel difficult.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Stormiest Weather in December',
'December can be the stormiest month, making automobile travel difficult.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storm Grows Strong',
'The storm is growing strong.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storms Crossing the Pacific',
'The storms are lining up across the Pacific Ocean.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storm''s Wind Delays Travel',
'The storm''s wind made car travel difficult on Tuesday.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storms'' Flooding Delays Travel',
'The storms'' flooding made auto travel difficult throughout December.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Children Run from Room',
'The children often storm out of the room when upset.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Boy Runs from Room',
'The boy storms out of the room when his sister changes the channel.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Girl Ran from Room',
'The girl stormed out of the room when her brother ate the cookie.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Children Running from Room',
'The children were storming out of the room when the lights went out.')
GO

create table #1 (
StormID INT NOT NULL IDENTITY,
StormHead NVARCHAR(50) NOT NULL,
StormBody NVARCHAR(MAX) NOT NULL,
)
go
INSERT INTO #1 (StormHead, StormBody)
VALUES('Boy Runs from Room',
'The boy storms out of the room when his sister changes the channel.')
INSERT INTO #1 (StormHead, StormBody)
VALUES('Girl Ran from Room',
'The girl stormed out of the room when her brother ate the cookie.')
INSERT INTO #1 (StormHead, StormBody)
VALUES('Children Running from Room',
'The children were storming out of the room when the lights went out.')

我的问题是如何提高如下链接的join顺序
select * from StormyWeather a join #1 b on a.StormBody = b.StormBody

需要补充的是
1. nvarchar(max)类型上 没办法加索引,
2.使用全文索引的话,好像没法支持 join操作
3.数据量是千万级或更高,单个表的大小目前大约10G,
4.这个查询只是个例子,用的最多的是在存储过程中,往表中插入此表没有的数据,其中判断条件中有这种大的列。


多谢多谢。。。



...全文
427 24 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
發糞塗牆 2013-11-14
  • 打赏
  • 举报
回复
的确改设计会好点,毕竟二进制的匹配开销也不小
肥龙上天 2013-11-14
  • 打赏
  • 举报
回复
引用 22 楼 DBA_Huangzj 的回复:
或者要不要试试把列在比较前转换成二进制来对比?
这也是个办法,不过经过各方面考虑,我还是选择修改表结构,加上一列存储MD5值:sys.fn_VarBinToHexStr(hashbytes(filePath, @src)). 还有更新前尽量缩小要操作的数据。 谢谢大家尤其版主“DBA_Huangzj ”的宝贵意见, 结贴。
發糞塗牆 2013-11-13
  • 打赏
  • 举报
回复
或者要不要试试把列在比较前转换成二进制来对比?
發糞塗牆 2013-11-13
  • 打赏
  • 举报
回复
而且我觉得CHECKSUM的效率也不好,我个人偏向用checksum(newid())来做随机排序,其他时间很少用
Leon_He2014 2013-11-13
  • 打赏
  • 举报
回复
一定要用千万级的表来做关联? 有没有办法先降数据量,再做关联?
肥龙上天 2013-11-13
  • 打赏
  • 举报
回复
引用 18 楼 DBA_Huangzj 的回复:
checksum是返回hash值,而且联机丛书上有说: The CHECKSUM value is dependent upon the collation. The same value stored with a different collation will return a different CHECKSUM value.
刚才花了点时间做了个测试,数据量少的话不好重现,所以不好把代码贴出来,我只把运行的结果贴出来,

\\tas\WBLUE\Common\Screenshots\FT\SCS\SKY\W6M3\Localization\7025.1007\int\5001\lb-LU\IE100\FileCloud_SharingGetALinkShortenedUrl.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\SCS\SKY\W6M3\Localization\7025.1007\int\5001\lb-LU\IE100\FileCloud_SharingGetALinkShortenedUrl.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\SCS\SKY\W6M3\Localization\7025.1007\int\5001\lb-LU\IE100\FileCloud_SharingGetALinkShortenedUrl.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\MSA\LiveIDWeb\W6M3\Localization\10.10.2013\Onebox\sr-Cyrl-CS\TFAUX_TFA_SwitchProofPage_SendSMS_OptIn_Snapped_Win8Wiz.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\MSA\LiveIDWeb\W6M3\Localization\10.10.2013\Onebox\sr-Cyrl-CS\TFAUX_TFA_SwitchProofPage_SendSMS_OptIn_Snapped_Win8Wiz.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\MSA\LiveIDWeb\W6M3\Localization\10.10.2013\Onebox\sr-Cyrl-CS\TFAUX_TFA_SwitchProofPage_SendSMS_OptIn_Snapped_Win8Wiz.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\MSA\LiveIDWeb\W6M3\Localization\10.10.2013\Onebox\sr-Cyrl-CS\TFAUX_TFA_SwitchProofPage_SendSMS_OptIn_Snapped_Win8Wiz.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\SCS\SKY\W6M3\Localization\7025.1007\int\5001\lb-LU\IE100\FileCloud_SharingGetALinkShortenedUrl.cap	-834524379
\\tas\WBLUE\Common\Screenshots\FT\SCS\SKY\W6M3\Localization\7024.1011\int\5001\pl-PL\IE100\OfficeCloud_BinaryConversionRetryableError.cap	1136973003
\\tas\WBLUE\Common\Screenshots\FT\SCS\SKY\W6M3\Localization\7108.1015\int\5001\gd-GB\IE100\OfficeCloud_GroupLeaveOptionsConfirmationDialog.cap	1136973003
\\tas\WBLUE\Common\Screenshots\FT\SP\FamilySafetyWeb\W6M1\Localization\10.23.2013\BVT\1\ig-NG\IE100\Email_ActivityReport9_Scroll_2.jpg	1938175911
\\tas\WBLUE\Common\Screenshots\FT\SP\FamilySafetyWeb\W6M1\Localization\10.23.2013\BVT\1\ig-NG\IE100\Email_ActivityReport7_Scroll_1.jpg	1938175911
注:不同的数据库,不同的表结构或者表大小的不同都可能影响checksum的值。 从上面可以看出,虽然filepath不同但 计算出的checksum值可能相同, 所以不能用checksum做验证方法。
發糞塗牆 2013-11-13
  • 打赏
  • 举报
回复
checksum是返回hash值,而且联机丛书上有说: The CHECKSUM value is dependent upon the collation. The same value stored with a different collation will return a different CHECKSUM value.
肥龙上天 2013-11-13
  • 打赏
  • 举报
回复
如果我没记错的话,checksum会产生重复数据,类似hashid,用md5相对更可靠些。
LongRui888 2013-11-13
  • 打赏
  • 举报
回复
引用 14 楼 yangsnow_rain_wind 的回复:
[quote=引用 12 楼 wufeng4552 的回复:] 为什么不针对StormBody 设计个int的键值的 其实你已经有了StormID 只是两个表都设置成了自增 可以考虑将子表的StormID改成非自增 与主表保持一致 用StormID作关联
上面我建的两个表只是个例子,实际我们这个系统比较复杂,要操作的表没有主键,每一个instance都有好多不同版本的图,也就是有好多path,所以需要用path做关联。 这种数据库设计肯定不太理想,不过要改变表结构可能影响太大,所以现在的看有没有补救措施,当然加上类似MD5列做标示也是种可行方法。问题看有没有更好的方法,所以来征求各位大牛的意见。。。。 thanks very much.[/quote] 通过checksum函数,得出一个值:


IF OBJECT_ID (N'StormyWeather', N'U') IS NOT NULL
DROP TABLE StormyWeather
GO

CREATE TABLE StormyWeather (
  StormID INT NOT NULL IDENTITY,
  StormHead NVARCHAR(50) NOT NULL,
  StormBody NVARCHAR(MAX) NOT NULL,  
)
GO

INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Stormy Weather Delays Travel',
  'The stormy weather made travel by motor vehicle difficult.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Stormier Weather on Monday',
  'The stormier weather on Monday made vehicle travel difficult.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Stormiest Weather in December',
  'December can be the stormiest month, making automobile travel difficult.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storm Grows Strong',
  'The storm is growing strong.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storms Crossing the Pacific',
  'The storms are lining up across the Pacific Ocean.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storm''s Wind Delays Travel',
  'The storm''s wind made car travel difficult on Tuesday.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Storms'' Flooding Delays Travel',
  'The storms'' flooding made auto travel difficult throughout December.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Children Run from Room',
  'The children often storm out of the room when upset.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Boy Runs from Room',
  'The boy storms out of the room when his sister changes the channel.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Girl Ran from Room',
  'The girl stormed out of the room when her brother ate the cookie.')
INSERT INTO StormyWeather (StormHead, StormBody)
VALUES('Children Running from Room',
  'The children were storming out of the room when the lights went out.')
GO

create table #1 (
  StormID INT NOT NULL IDENTITY,
  StormHead NVARCHAR(50) NOT NULL,
  StormBody NVARCHAR(MAX) NOT NULL,  
)
go 
INSERT INTO #1 (StormHead, StormBody)
VALUES('Boy Runs from Room',
  'The boy storms out of the room when his sister changes the channel.')
INSERT INTO #1 (StormHead, StormBody)
VALUES('Girl Ran from Room',
  'The girl stormed out of the room when her brother ate the cookie.')
INSERT INTO #1 (StormHead, StormBody)
VALUES('Children Running from Room',
  'The children were storming out of the room when the lights went out.')
go


--用checksum计算一个值
select CHECKSUM(StormBody)
from StormyWeather
/*
(无列名)
318284405
-1996609556
-957497181
942488834
-1966874030
1290197037
613467569
706926295
-1408726896
1805667295
136736880
*/
發糞塗牆 2013-11-13
  • 打赏
  • 举报
回复
为表关注,围观一下,不过据本菜水平,目前还没有其他方案
肥龙上天 2013-11-13
  • 打赏
  • 举报
回复
引用 12 楼 wufeng4552 的回复:
为什么不针对StormBody 设计个int的键值的 其实你已经有了StormID 只是两个表都设置成了自增 可以考虑将子表的StormID改成非自增 与主表保持一致 用StormID作关联
上面我建的两个表只是个例子,实际我们这个系统比较复杂,要操作的表没有主键,每一个instance都有好多不同版本的图,也就是有好多path,所以需要用path做关联。 这种数据库设计肯定不太理想,不过要改变表结构可能影响太大,所以现在的看有没有补救措施,当然加上类似MD5列做标示也是种可行方法。问题看有没有更好的方法,所以来征求各位大牛的意见。。。。 thanks very much.
發糞塗牆 2013-11-12
  • 打赏
  • 举报
回复
更好的方法应该匹配标识列,一般插入的时候应该判断行是否存在,而不是某个数据是否存在
水族杰纶 2013-11-12
  • 打赏
  • 举报
回复
为什么不针对StormBody 设计个int的键值的 其实你已经有了StormID 只是两个表都设置成了自增 可以考虑将子表的StormID改成非自增 与主表保持一致 用StormID作关联
LongRui888 2013-11-12
  • 打赏
  • 举报
回复
引用 2 楼 yangsnow_rain_wind 的回复:
[quote=引用 1 楼 DBA_Huangzj 的回复:] 那么长的类型还要关联,貌似不合理吧?
其实这个字段存的是一个文件路径,类似如下 \\FT\SCS\SKY\W6M1\Localization\6419.0107\int\5001\prs-AF\IE90\Photos_QuotaFull-ManageStorage.jpg \\FT\EP\CMX\W5M4\Localizability\17.0.1541.0607\bvt\5005\en-us\IE90\SM_SLCMainViewNoData.jpg \\FT\DRX\SkyDriveClient\W6M1\Localization\2013.05.03\INT\3037.0503\mi-nz\Selective_Sync_Error_From_FRE_Unprovisioned.cap \\FT\WC\Hotmail\W6M2\Localization\17.2.6710.1000\INT\ti-ET\WC_Hotmail_MaxRecipientsScreenCapture.cap \\FT\EP\CMX\W5M4\Localizability\17.0.1546.0612\bvt\5005\kok-IN\IE90\SM_SLCMainView_W6_M2_IR02_NewStrings.jpg \\FT\SCS\SKY\W6M2\Localization\6711.1002\int\5001\chr-Cher-US\IE100\FileCloud_RearrangeModeMultiSelect.cap \\FT\EP\CMX\W5M4\Localizability\17.0.1533.0603\bvt\5005\ta-IN\IE90\SM_SLCMainViewNoData.jpg \\FT\SCS\SKY\W6M2\Localization\6711.1008\int\5001\bn-BD\IE100\DeviceCloud_QuotaFull-SetView.cap \\FT\SCS\SKY\W6M2\Localization\6611.1008\int\5001\fil-PH\IE100\FileCloud_PanoramaFolderThumbnail.cap \\FT\DRX\SkyDriveClient\W6M1\Localization\2013.08.16\INT\3145.0816\ml-in\DRX_SkyDrive_TaskDialog_ProcessRunningInElevatedMode.cap 当需要再往此表中添加数据时要判断是否此图片已经存在。 数据库设计可能有些问题,但我现在没办法修改。看有没有好的解决方案。[/quote] 能不能修改一下,改为比如,nvarchar(1000),因为如果是存储文件路径,应该不需要占用那么多的字节的把
發糞塗牆 2013-11-12
  • 打赏
  • 举报
回复
欢迎共享
肥龙上天 2013-11-12
  • 打赏
  • 举报
回复
这确实是个头痛但又很常碰到的问题。我再研究研究,有结果了,一定会尽快帖上。。
發糞塗牆 2013-11-12
  • 打赏
  • 举报
回复
其实思路还是缩短查找的范围。有些系统也会额外做一个索引表(和数据库的索引不一样),查东西的时候先查索引表,然后定位最终的表或者分区,当然在2005以后出现的分区表大概也能实现类似的功能
肥龙上天 2013-11-12
  • 打赏
  • 举报
回复
引用 5 楼 yangsnow_rain_wind 的回复:
[quote=引用 4 楼 DBA_Huangzj 的回复:] 比如你的例子,可能你需要提取一些标识性的值来单独一个列,然后进行匹配
您的意思是我在这个表中加一个新列,如:UniqueContent, 里面存储的是对这个path列的MD5计算值,然后在计算后者比较时使用该列代替原理的path列,这是个好办法,我以前想过这样,但这样最大的一个弊端是我需要修改几乎所有的程序,所以当时就抛弃这个方案了,敢问大牛还有什么别的可行方案吗? 感谢,感谢。。。[/quote] 写错字了,然后再以后对此表进行计算或者match时使用UniqueContent列代替path列
發糞塗牆 2013-11-12
  • 打赏
  • 举报
回复
设计问题的确是很头痛的,但是你也想想,简单匹配每个数据根本不可行,何况你那千万级的数据量。我觉得我刚才说的,也就是你5楼说的那个方案可能是我目前知道的最好的方案,工作量的确不少,但是有些时候是没办法
肥龙上天 2013-11-12
  • 打赏
  • 举报
回复
引用 4 楼 DBA_Huangzj 的回复:
比如你的例子,可能你需要提取一些标识性的值来单独一个列,然后进行匹配
您的意思是我在这个表中加一个新列,如:UniqueContent, 里面存储的是对这个path列的MD5计算值,然后在计算后者比较时使用该列代替原理的path列,这是个好办法,我以前想过这样,但这样最大的一个弊端是我需要修改几乎所有的程序,所以当时就抛弃这个方案了,敢问大牛还有什么别的可行方案吗? 感谢,感谢。。。
加载更多回复(4)

34,838

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server相关内容讨论专区
社区管理员
  • 基础类社区
  • 二月十六
  • 卖水果的net
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧