【问题标题】:SQL Server XML Joining using Exist() for Performance使用 Exist() 进行 SQL Server XML 连接以提高性能
【发布时间】:2017-11-29 03:45:30
【问题描述】:

我正在尝试改进一个需要一些时间的查询(可能找错了地方,但我知道 XML 是一个很好的起点,所以这就是我现在要做的)。

XML 看起来像这样;

<BlockModels>
    <BlockModel id="1"></BlockModel>
</BlockModels>

很简单,不幸的是我无法控制。它是固定格式/定义。

SQL 语句如下所示:

SELECT bm.Name, b.BlockModel.value('(./@id)[1]', 'INT')
FROM @iBlockModels.nodes('/BlockModels/BlockModel') AS b(BlockMOdel)
INNER JOIN BlockModel AS bm
    ON (bm.Block_Model_Id = b.BlockModel.value('(./@id)[1]', 'INT'))

我已遵循此文档; https://docs.microsoft.com/en-us/sql/t-sql/xml/value-method-xml-data-type,这是我从中获得静态类型 ([1]) 语法的地方。不过,性能似乎没有太大提升。

在上述文档中,D 点提到使用exist() 而不是value(),但该示例不包括两个表之间的连接。我怀疑这是不可能的,我尝试将上面的内容转换为类似示例的内容导致它失败了。任何人都可以协助转换吗?或者指出我可以做些什么来帮助提高性能?

【问题讨论】:

  • 它为您提供了如何执行此操作的示例。在sql:column 部分中,您只需输入列名。例如INNER JOIN BlockModel AS bm ON b.BlockModel.exist('.[@id=sql:column("bm.Block_Model_Id")') = 1
  • 啊,好的,感谢您的澄清,我对方括号/根部分感到困惑。遗憾的是没有性能提升。
  • 就性能而言,它可能在很大程度上取决于 blockmodel 表中的 block_model_id 列上是否有索引(假设没有 - 如果有并且您仍然遇到性能问题那么根据您提供的查询,我看不到明显的原因)。您也可能通过加入的价值方法获得更好的性能。
  • 是的,它们看起来一样,也许这就是最好的。鉴于它只需要 ID,也许我可以将它(它们)拉出来用于 IN 语句?
  • 如果您在列列表中获取 bm.Block_Model_Id 而不是再次从 XML 中获取值,您的原始查询可能会好得多。

标签: sql-server xml


【解决方案1】:

将 id 分解为临时表并在连接中使用它。

create table #bm(ID int primary key);

insert into #bm (ID)
select T.X.value('@id', 'int')
from @iBlockModels.nodes('/BlockModels/BlockModel') as T(X);

select bm.Name, bm.Block_Model_Id
from dbo.BlockModel as bm
  inner join #bm as bm2
    on bm.Block_Model_Id = bm2.id;

drop table #bm;

【讨论】:

    【解决方案2】:

    在你的情况下,它不会有任何区别。带有(SomePath)[1] 的XPath 查找第一次出现并返回该值。 .exist() 的作用完全相同,但只检查存在性。在您的情况下,您需要 @id 的值,如果该属性存在,则检查是不够的......但是两个调用都会立即返回一个值(是/否或数字)。

    在多次出现的情况下,差异是相关的。如果您的问题很简单至少有一次发生吗?.exist() 会比.nodes() 等更快。

    您的加入需要@id 的值,因此您必须一遍又一遍地阅读它。在JOIN 中,这不能很快。有XML索引,但是I would not recommend to use them

    如果性能非常重要,您可以尝试以下方法:

    CREATE TABLE tmpTest(ID INT IDENTITY,SomeValue VARCHAR(100),YourXml XML,RefId INT);
    GO
    

    --创建一个触发器以在任何插入或更新时写出所需的值

    CREATE TRIGGER dbo.InsertUpdate_tmpTest ON tmpTest
    FOR INSERT,UPDATE
    AS
    BEGIN
        WITH UpdateableCTE AS
        (
            SELECT t.RefID AS OldValue
                  ,i.YourXml.value(N'/BlockModels[1]/BlockModel[1]/@id',N'int') AS NewValue
            FROM tmpTest AS t
            INNER JOIN inserted AS i ON i.ID=t.ID
        )
        UPDATE UpdateableCTE SET OldValue=NewValue;
    END
    GO
    

    --现在可以放置索引了

    CREATE INDEX IX_tmpTest_RefId ON tmpTest(RefId);
    GO
    

    --一些行为测试

    INSERT INTO tmpTest(SomeValue,YourXml) VALUES
     ('This is row 1','<BlockModels><BlockModel id="1"></BlockModel></BlockModels>') --ref 1
    ,('This is row 2','<BlockModels><BlockModel id="1"></BlockModel></BlockModels>') --ref 1
    ,('This is row 3','<BlockModels><BlockModel id="2"></BlockModel></BlockModels>') --ref 2
    ,('This is row 4','<BlockModels><BlockModel id="3"></BlockModel></BlockModels>');--ref 3
    GO
    
    SELECT * FROM tmpTest;
    
    UPDATE tmpTest SET YourXml='<BlockModels><BlockModel id="99"></BlockModel></BlockModels>' WHERE ID=4;
    
    SELECT * FROM tmpTest;
    
    UPDATE tmpTest SET YourXml='<BlockModels><BlockModel id="0"></BlockModel></BlockModels>' WHERE ID IN(1,2,3);
    
    SELECT * FROM tmpTest;
    
    GO
    

    --清理

    DROP TRIGGER dbo.InsertUpdate_tmpTest;
    DROP TABLE tmpTest;
    

    这个“计算”列RefId 在连接中会非常快。

    关于“计算列”:遗憾的是 - 在您的情况下 - 不可能将计算列(既不是 direct 也不是 persisted)与索引一起使用。 ..

    【讨论】:

      猜你喜欢
      • 2014-08-29
      • 1970-01-01
      • 2013-02-04
      • 1970-01-01
      • 2020-11-20
      • 1970-01-01
      • 2012-06-16
      • 1970-01-01
      相关资源
      最近更新 更多