我有几个观察和建议:
- 你可能会得到相似的基准测试结果,因为SQL Server进行了优化。为了正确比较查询的性能,你需要避免这些优化。但在此之前,请确保只在测试环境中尝试。我重复一遍,不要在生产环境中执行此操作!
- 针对前缀列进行查询肯定会提高查询性能。
- 由于你查询的前缀是固定长度(19),所以应该使用BINARY(19)数据类型来表示该列。这里的一些建议将创建一个VARBINARY列。
- 如果可能的话,我个人会避免在表上放置触发器。C#示例中有一种可能的替代方法。
- 在我的测试中,使用等号查询比SUBSTRING更快,而SUBSTRING又比LEFT更快。
我得出这个结论的方法是将约15000个文件加载到测试数据库中,并执行不同类型的查询(多次),得到以下结果:
- 使用前缀列进行相等性检查 -> 191毫秒
- SUBSTRING -> 20448毫秒 = 慢107倍
- LEFT -> 34091毫秒 = 慢178倍
这是一个创建测试表的SQL脚本:
CREATE TABLE [dbo].[Files]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Path] [varchar](260) NOT NULL,
[Prefix] [binary](19) NOT NULL,
[AllBytes] [varbinary](MAX) NOT NULL,
CONSTRAINT [PK_Files] PRIMARY KEY CLUSTERED ([Id] ASC)
)
现在是查询时间(请在不同的选项卡中运行以下内容):
1.
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS;
GO
DECLARE @pattern BINARY(19)
SET @pattern = 0x4D5A90000300000004000000FFFF0000B80000
SELECT [Path]
FROM [dbo].[Files]
WHERE Prefix <> @pattern
2.
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS;
GO
DECLARE @pattern BINARY(19)
SET @pattern = 0x4D5A90000300000004000000FFFF0000B80000
SELECT [Path]
FROM [dbo].[Files]
WHERE SUBSTRING(AllBytes, 0, 19) <> @pattern
3.
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS;
GO
DECLARE @pattern VARBINARY(19)
SET @pattern = 0x4D5A90000300000004000000FFFF0000B80000
SELECT [Path]
FROM [dbo].[Files]
WHERE LEFT(AllBytes, 19) <> @pattern
现在,下面是一段创建测试数据的代码:
using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
namespace SO
{
class Program
{
static void Main(string[] args)
{
const string sqlConnectionString = @"Data Source=yourServer;Initial Catalog=databaseName;Integrated Security=True";
const string folderToProcess = @"C:\Program Files";
const int prefixLength = 19;
using (var connection = new SqlConnection(sqlConnectionString))
{
connection.Open();
foreach (var filePath in Directory.GetFiles(folderToProcess, "*.*", SearchOption.AllDirectories))
{
Console.WriteLine(filePath);
byte[] allBytes = File.ReadAllBytes(filePath);
if (allBytes.Length < prefixLength) { continue; }
byte[] prefix = new byte[prefixLength];
Array.Copy(allBytes, prefix, prefixLength);
using (var command = new SqlCommand("INSERT INTO dbo.Files (Path, Prefix, AllBytes) Values(@path, @prefix, @allBytes)", connection))
{
command.Parameters.Add("@path", SqlDbType.NVarChar, 260).Value = filePath;
command.Parameters.Add("@prefix", SqlDbType.Binary, prefixLength).Value = prefix;
command.Parameters.Add("@allBytes", SqlDbType.VarBinary, allBytes.Length).Value = allBytes;
command.ExecuteNonQuery();
}
}
connection.Close();
}
Console.WriteLine("All Done !");
Console.ReadLine();
}
}
}
WHERE CAST(r.payload as varbinary(19)) <> @pattern
- Magnus@pattern
声明为仅为VARBINARY
本身,还是VARBINARY(19)
?如果您没有指定最大长度,则可能会出现问题,即使不是问题的根本原因,因为在声明VARCHAR
/NVARCHAR
/VARBINARY
变量时的默认长度只有1,例如,如果实际执行DECLARE @Pattern VARBINARY;
,则会得到一个VARBINARY(1)
变量。此外,虽然可能不是问题,但SUBSTRING
的最小起始值为1,而不是0。 - Solomon RutzkySUBSTRING
是正确的,而CAST
是错误的。但实际上恰恰相反。通过使用0
作为SUBSTRING
的起始索引,您实际上是截掉了最后一个字符,得到了18个字节而不是19个字节。CAST
(或CONVERT
)正确地返回了19个字节。 - Solomon Rutzky