在 Sql Server 索引(物化)视图中创建嵌套 XML

Vac*_*ano 3 xml sql-server materialized-view sql-server-2012

此页面https://msdn.microsoft.com/en-us/library/ms188276.aspx表示如果您想要嵌套的 XML(即 XML 树),那么您需要像这样设置查询:

SELECT Col1, 
       Col2, 
       ( SELECT Col3, Col4 
        FROM  T2
        WHERE T2.Col = T1.Col
        ...
        FOR XML AUTO, TYPE )
FROM T1
WHERE ...
FOR XML AUTO, TYPE;
Run Code Online (Sandbox Code Playgroud)

(使用子查询)

这与索引视图的要求完全不一致(不允许子查询)。

有没有办法将这两个功能结合在一起?(有一个带有 XML 树的索引视图?)

我的问题的不足是: 如何在不使用任何子查询或联合的情况下从许多表创建 XML 树(即嵌套的 XML 节点)。(因此它将与 SQL Server 索引视图一起使用。)

仅供参考:不确定是否重要,但我的特定查询有超过 10 个级别的 xml 树(从一堆不同的表中提取)。

Ril*_*jor 11

简短的回答:你不能。

长答案:

这里发生了一些事情。

首先,您可能低估了XML AUTO. 它将“自动”提供一定数量的嵌套。实际上,您提供的示例可以在没有嵌套 XML 生成的情况下进行处理。

让我们制作一些测试表和数据:

USE tempdb;

DROP TABLE IF EXISTS T1;
DROP TABLE IF EXISTS T2;

CREATE TABLE T1
(
    Col1 int,
    Col2 varchar(50),
    [Col] char(1)
);

CREATE TABLE T2
(
    [Col] char(1),
    Col3 varchar(50),
    Col4 varchar(50)
);

INSERT INTO T1 (Col1, Col2, [Col]) VALUES
    (1, 'Test1', 'A'), (2, 'Test2', 'B');
INSERT INTO T2 ([Col], Col3, Col4) VALUES
    ('A','1x','1y'), ('A','2x','2y'), ('B','3x','3y'), ('B','4x','4y'), ('B','5x','5y');
Run Code Online (Sandbox Code Playgroud)

您的查询:

SELECT
    Col1,
    Col2,
    (
        SELECT      Col3, Col4
        FROM        T2
        WHERE       T2.Col = T1.Col
        FOR XML AUTO, TYPE
    )
FROM        T1
FOR XML AUTO, TYPE;
Run Code Online (Sandbox Code Playgroud)

产生与没有任何嵌套的结果相同的结果:

SELECT
    Col1,
    Col2,
    Col3,
    Col4
FROM        T1
JOIN        T2
ON          T2.Col = T1.Col
FOR XML AUTO, TYPE;
Run Code Online (Sandbox Code Playgroud)

他们都生产:

<T1 Col1="1" Col2="Test1">
  <T2 Col3="1x" Col4="1y" />
  <T2 Col3="2x" Col4="2y" />
</T1>
<T1 Col1="2" Col2="Test2">
  <T2 Col3="3x" Col4="3y" />
  <T2 Col3="4x" Col4="4y" />
  <T2 Col3="5x" Col4="5y" />
</T1>
Run Code Online (Sandbox Code Playgroud)

(如果您有更复杂的或自定义的嵌套要求,那将会崩溃。)

不管这个特定的例子如何,我认为没有一种有效的方法可以将(动态)XML 放入索引视图中。

首先,“索引视图”实际上是具有唯一聚集索引的视图。您不能在 XML 列上创建“普通”索引。

假设您尝试:

CREATE TABLE T3
(
    x xml
);
CREATE UNIQUE CLUSTERED INDEX IX_T3 ON T3 (x);
GO
Run Code Online (Sandbox Code Playgroud)

你会得到一个错误:

消息 1977,级别 16,状态 1,第 73 行无法在表 'T3' 上创建索引 'IX_T3'。只能在 XML 列“x”上创建 XML 索引。

XML 索引怎么样?好:

您不能在视图中的 xml 列、具有 xml 列的表值变量或 xml 类型变量上创建 XML 索引,无论是主索引还是辅助索引。

创建 XML 索引 (Transact-SQL)

因此,为了使用视图实现 XML,它不能是唯一的列。您需要一个“普通”列来创建唯一聚集索引。获取普通列的一种方法是使用子查询创建 XML。但正如您所指出的,如果您尝试这样做:

CREATE VIEW V1
(
    n,
    x
)
WITH SCHEMABINDING
AS
SELECT 
    1,
    (
        SELECT 'Test' AS 'Col'
        FOR XML PATH, TYPE
    )
GO

CREATE UNIQUE CLUSTERED INDEX IX_V1 ON V1 (n);
GO
Run Code Online (Sandbox Code Playgroud)

你会得到这个错误:

消息 10127,级别 16,状态 1,第 83 行无法在视图“tempdb.dbo.V1”上创建索引,因为它包含一个或多个子查询。考虑将视图更改为仅使用联接而不是子查询。或者,考虑不索引此视图。

您可能认为可以通过创建一个没有子查询的外部视图调用原始视图来作弊。但如果你尝试:

CREATE VIEW V2
(
    n,
    x
)
WITH SCHEMABINDING
AS
SELECT
    n,
    x
FROM    dbo.V1;
GO

CREATE UNIQUE CLUSTERED INDEX IX_V2 ON V2(n);
GO
Run Code Online (Sandbox Code Playgroud)

你会得到一个错误:

消息 1937,级别 16,状态 1,第 98 行无法在视图“tempdb.dbo.V2”上创建索引,因为它引用了另一个视图“dbo.V1”。考虑在索引视图定义中手动扩展引用视图的定义。

所以你不能嵌套这些视图,也不能使用子查询。您也不能使用 APPLY 运算符,因此您不能使用表值用户定义函数(也不能有效地使用nodesXML 函数)。

但是您可以使用确定性标量值函数。因此,您可以构建一些基本的 XML。例如:

CREATE FUNCTION F1
(
    @Col1 int,
    @Col2 varchar(50)
)
RETURNS xml
WITH SCHEMABINDING
AS
BEGIN
    RETURN
        (
            SELECT
                @Col1,
                @Col2
            FOR XML PATH(''), TYPE
        )
END
GO

CREATE VIEW V3
(
    n,
    x
)
WITH SCHEMABINDING
AS
SELECT
    Col1,
    dbo.F1(T1.Col1, T1.Col2)
FROM    dbo.T1 AS T1;
GO

CREATE UNIQUE CLUSTERED INDEX IX_V3 ON V3(n);
GO
Run Code Online (Sandbox Code Playgroud)

虽然您可以在函数内创建复杂的多级 XML,但该函数不能引用任何表(或者它不是确定性的)。例如,这个:

CREATE FUNCTION F2
(
    @Col1 int,
    @Col2 varchar(50),
    @Col char(1)
)
RETURNS xml
WITH SCHEMABINDING
AS
BEGIN
    RETURN
        (
            SELECT
                @Col1 AS Col1,
                @Col2 AS Col2,
                (
                    SELECT      Col3, Col4
                    FROM        dbo.T2 AS T2
                    WHERE       T2.Col = @Col
                    FOR XML AUTO, TYPE
                )
            FOR XML PATH, TYPE
        )
END
GO

CREATE VIEW V4
(
    n,
    x
)
WITH SCHEMABINDING
AS
SELECT
    Col1,
    dbo.F2(T1.Col1, T1.Col2, T1.Col)
FROM    dbo.T1 AS T1;
GO

CREATE UNIQUE CLUSTERED INDEX IX_V4 ON V4(n);
GO
Run Code Online (Sandbox Code Playgroud)

导致此错误:

消息 10133,级别 16,状态 1,第 181 行无法在视图“tempdb.dbo.V4”上创建索引,因为视图引用的函数“dbo.F2”执行用户或系统数据访问。

因此函数不能去获取它需要的信息。它必须提供信息。而且根本没有办法(我能想到的)从多行收集数据并将其提供给函数。您可以提供一个确定性函数 XML 进行操作,但您不能在没有子查询的情况下创建基础 XML。

简而言之,我认为没有任何方法可以使用索引视图来具体化包含来自任何表中多行数据的 XML。