【问题标题】:EndsWith SQL query for VARBINARY(MAX) FILESTREAM columns [closed]EndsWith SQL 查询 VARBINARY(MAX) FILESTREAM 列[关闭]
【发布时间】:2018-04-19 08:53:47
【问题描述】:

由于WHERE 子句中的条件,目前我有两个 SQL 查询提议,它们在同一 VARBINARY 列上多次调用 DATALENGTH

在同一查询中对DATALENGTH 的多次调用是否会影响性能,或者DATALENGTH 计算的结果是否可以由Microsoft SQL Server 优化/缓存?

目标是为VARBINARY(MAX) FILESTREAM 列构建一个EndsWith SQL 查询。

在我的情况下,我不能向表中添加任何额外的索引或列,但是 我想了解可能的替代方案。

-- Table with BinData
DECLARE @foo TABLE(BinData VARBINARY(MAX));
INSERT INTO @foo (BinData) VALUES
(0x00001125), (0x00112500), (0x11250000),
(0x00000011), (0x00001100), (0x00110000), (0x11000000),
(0x112500001125);

-- BinKey
DECLARE @BinKey VARBINARY(MAX) = 0x1125;

-- Search for [BinData] ending with @BinKey --

到目前为止我已经尝试过什么......

提案 A - REVERSE BinKeyBinData,然后使用 CHARINDEX

SELECT * FROM @foo WHERE
    DATALENGTH(BinData) >= DATALENGTH(@BinKey) AND
    CHARINDEX
    (
        CONVERT(VARCHAR(MAX), @BinKey),
        CONVERT(VARCHAR(MAX), BinData)
    ) = 1 + DATALENGTH(BinData) - DATALENGTH(@BinKey);

Proposal B - 剪切 BinData 的结尾部分并将其与 BinKey 进行比较。

SELECT * FROM @foo WHERE
    DATALENGTH(BinData) >= DATALENGTH(@BinKey) AND
    SUBSTRING(
        BinData,
        1 + DATALENGTH(BinData) - DATALENGTH(@BinKey),
        DATALENGTH(@BinKey)
    ) = @BinKey;

James L. 的建议 - 只需使用 RIGHT 函数即可。

SELECT * FROM @foo WHERE
    RIGHT(BinData, DATALENGTH(@BinKey)) = @BinKey;

上面列出的查询的结果必须是:

0x00001125
0x112500001125

有没有更好的算法呢?对于较大的 blob,它的性能如何?

【问题讨论】:

  • SQL Server 确实知道DATALENGTHdeterministic
  • @Damien_The_Unbeliever:是的,确定性的......但这是否意味着使用同一列作为 WHERE CLAUSE 中的参数多次调用 DATALENGTH 不会导致额外的计算?
  • 这意味着它有机会将它的调用次数减少到最低限度。您需要检查特定执行计划以供您的查询确定。
  • 在内部,VAR 值与它们的长度一起存储,所以我看不出DATALENGTH 会是什么,除了恒定的时间,这使得它在很大程度上无关紧要你多久采取的长度相同的列/变量。但一如既往,测量为王。构建大块不是火箭科学。 (对于FILESTREAM,可能会有所不同,但我非常怀疑——文件系统不会白白记录文件长度!)
  • 这个问题更适合dba.stackexchange.com

标签: sql sql-server varbinary sqlfilestream datalength


【解决方案1】:

我不确定如何分析这将如何执行,但这可以满足您的要求:

-- Table with BinData
DECLARE @foo TABLE(BinData VARBINARY(MAX));
INSERT INTO @foo (BinData) VALUES
(0x00001125), (0x00112500), (0x11250000),
(0x00000011), (0x00001100), (0x00110000), (0x11000000),
(0x112500001125);

-- BinKey
DECLARE @BinKey VARBINARY(MAX) = 0x1125;

-- Search for [BinData] ending with @BinKey --
select *
from   @foo
where  right(BinData, datalength(@BinKey)) = @BinKey

返回以下内容:

BinData
----------------
0x00001125
0x112500001125

我添加了 10,000 条记录,每条记录大小约为 1kb。查询所有这些需要 0.020 秒。我还尝试在 10,000 条 1kb 记录之后添加 10 条 1MB 大小的记录。运行查询花费了 0.037 秒。当 1kb 或 1MB 的记录以 0x1125 结尾时,会增加一点执行时间,因为 SSMS 必须在输出窗口中接收并格式化记录。以 0x1125 结尾的 50 个 1kb 行需要 0.057 秒才能运行。当 1MB 行之一以 0x1125 结束时,运行需要 0.110 秒。这不是很科学,但它似乎表现得很好。

【讨论】:

  • LEN() 有时无法计算二进制数据的正确长度,特别是在数据以 0x20 开始/结束的情况下,尽管使用 RIGHT 是一个非常好的点(我已经添加了您的建议我的问题)。 DATALENGTH 解决了这个问题。 -- 试试这个:SELECT(LEN(0x2020252020), DATALENGTH(0x2020252020));
  • 我有很多 FILESTREAM 数据。这种方法似乎也适用于 FILESTREAM 字段。
  • 我明白你对len() 的意思。这似乎是 MS 应该修复的错误。
  • 正如微软所说:LEN 不包括尾随空格。如果这是一个问题,请考虑使用不修剪字符串的 DATALENGTH (Transact-SQL) 函数。 - docs.microsoft.com/en-us/sql/t-sql/functions/…
  • @JamesL。他们做到了,修复程序称为DATALENGTH。用于比较的尾随空格“不计算在内”这一事实现在已被奉为神圣,并清楚地记录在案——请注意'a ' = 'a',所以LEN('a ') = LEN('a')。尽管LEN 名义上是对二进制值进行操作,但它本质上是一个字符串函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多