SQL 选择 FOR XML 到 Solr 文档中

发布于 2024-12-04 04:31:53 字数 651 浏览 0 评论 0原文

我正在尝试获取 SQL select 语句来生成符合 Solr 标准的 XML。

给定一个像这样的表:

id | name
---------
 1 | one
 2 | two
 3 | three

我需要一个类似于(有或没有根节点)的结果:

<add>
  <doc>
    <field name="id">1</field>
    <field name="name">one</field>
  </doc>
  <doc>
    <field name="id">2</field>
    <field name="name">two</field>
  </doc>
  <doc>
    <field name="id">3</field>
    <field name="name">three</field>
  </doc>
</add>

是否可以使用 FOR XML 查询生成该结构,或者我需要 XSLT 或其他一些匹配该模式的机制?

I'm trying to get a SQL select statement to generate XML which conforms to the Solr standard.

Given a table like:

id | name
---------
 1 | one
 2 | two
 3 | three

I need a result which is like (with or without the root node):

<add>
  <doc>
    <field name="id">1</field>
    <field name="name">one</field>
  </doc>
  <doc>
    <field name="id">2</field>
    <field name="name">two</field>
  </doc>
  <doc>
    <field name="id">3</field>
    <field name="name">three</field>
  </doc>
</add>

Is it possible to generate that structure using a FOR XML query, or will I need an XSLT or some other mechanism to match that schema?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

孤独难免 2024-12-11 04:31:53

这是使用构造函数的稍微不同的方法。

DECLARE @sample TABLE
(
    [id]    int         NOT NULL,
    [name]  varchar(50) NOT NULL
);
INSERT INTO @sample ([id], [name])
SELECT 1, 'one' UNION ALL
SELECT 2, 'two' UNION ALL
SELECT 3, 'three';

SELECT
    CONVERT(xml, N'').query
    (N'
        <doc>
        {
            element field
            {
                attribute name {"id"},
                text{sql:column("id")}
            },
            element field
            {
                attribute name {"name"},
                text{sql:column("name")}
            }
        }
        </doc>
    ')
FROM
    @sample
FOR XML PATH(N''), ROOT(N'add');

* 编辑:只是想到了另一种方法来做到这一点(但仍然需要提前了解列)*
同样,我不确定这两种方法对性能的影响。

SELECT
    (
        SELECT
            'id' AS [@name],
            [id] AS [data()]
        FOR XML PATH('field'), TYPE
    ) AS [*],
    (
        SELECT
            'name' AS [@name],
            [name] AS [data()]
        FOR XML PATH('field'), TYPE
    ) AS [*]
FROM
    @sample
FOR XML PATH(N'doc'), ROOT(N'add');

* 更新 2:受 Aaron Bertrand 评论启发的动态但非性能方法 *

这是 Aaron 在评论中引用的帖子中描述的方法的概念证明。 (它在较大的数据集上表现非常糟糕)

-- Inspired by Aaron Bertrand's comment
WITH [cte_KVP]
AS
(
    -- Generating Key/Value pairs for columns in a table
    -- Courtesey of Mikael Eriksson (http://stackoverflow.com/questions/7341143/flattening-of-a-1-row-table-into-a-key-value-pair-table/)
    SELECT
        [T2].[N].value(N'local-name(.)', N'sysname')    AS [Key],
        [T2].[N].value(N'.', N'nvarchar(max)')          AS [Value],
        [T2].[N].value(N'../GROUP[1]', N'int')          AS [GROUP] -- 3. Used for to group the key/value pairs per row
    FROM
        (
            SELECT
                *,
                ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [GROUP] -- 1. Generating a simple "identity" value.
            FROM
                @sample
            FOR XML PATH(N'Row'), TYPE -- 2. Adding the 'Row' to the path separates each row, and allows us to backtrack via xpath to get the "GROUP" id
        ) AS [T1]([x])
    CROSS APPLY
        [T1].[x].nodes(N'Row/*') AS [T2]([N])
    WHERE
        [T2].[N].value(N'local-name(.)', N'sysname') <> N'GROUP'
)
SELECT
    [InnerNodes].[xml] AS [*]
FROM
    (
        -- Probably preferable to use a table of numbers here
        SELECT DISTINCT
            [GROUP]
        FROM
            [cte_KVP]
    ) AS [Numbers]([Number])
CROSS APPLY
    (
        -- Generating the xml fragment specified by OP
        SELECT
            [cte_KVP].[Key]     AS [@name],
            [cte_KVP].[Value]   AS [data()]
        FROM
            [cte_KVP]
        WHERE
            [cte_KVP].[GROUP] = [Numbers].[Number]
        FOR XML PATH(N'field'), ROOT(N'doc'), TYPE
    ) AS [InnerNodes]([xml])
FOR XML PATH(N''), ROOT(N'add');

Here's a slightly different way using constructors.

DECLARE @sample TABLE
(
    [id]    int         NOT NULL,
    [name]  varchar(50) NOT NULL
);
INSERT INTO @sample ([id], [name])
SELECT 1, 'one' UNION ALL
SELECT 2, 'two' UNION ALL
SELECT 3, 'three';

SELECT
    CONVERT(xml, N'').query
    (N'
        <doc>
        {
            element field
            {
                attribute name {"id"},
                text{sql:column("id")}
            },
            element field
            {
                attribute name {"name"},
                text{sql:column("name")}
            }
        }
        </doc>
    ')
FROM
    @sample
FOR XML PATH(N''), ROOT(N'add');

* EDIT: Just thought of another way to do this (but still requires knowledge of the columns ahead of time) *
Again, i'm uncertain of the performance implications of either approach.

SELECT
    (
        SELECT
            'id' AS [@name],
            [id] AS [data()]
        FOR XML PATH('field'), TYPE
    ) AS [*],
    (
        SELECT
            'name' AS [@name],
            [name] AS [data()]
        FOR XML PATH('field'), TYPE
    ) AS [*]
FROM
    @sample
FOR XML PATH(N'doc'), ROOT(N'add');

* UPDATE 2: Dynamic yet non-performant method inspired by Aaron Bertrand's comments *

This was a proof of concept of the method described in the post Aaron referenced in his comments. (It performs horribly on larger datasets)

-- Inspired by Aaron Bertrand's comment
WITH [cte_KVP]
AS
(
    -- Generating Key/Value pairs for columns in a table
    -- Courtesey of Mikael Eriksson (http://stackoverflow.com/questions/7341143/flattening-of-a-1-row-table-into-a-key-value-pair-table/)
    SELECT
        [T2].[N].value(N'local-name(.)', N'sysname')    AS [Key],
        [T2].[N].value(N'.', N'nvarchar(max)')          AS [Value],
        [T2].[N].value(N'../GROUP[1]', N'int')          AS [GROUP] -- 3. Used for to group the key/value pairs per row
    FROM
        (
            SELECT
                *,
                ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [GROUP] -- 1. Generating a simple "identity" value.
            FROM
                @sample
            FOR XML PATH(N'Row'), TYPE -- 2. Adding the 'Row' to the path separates each row, and allows us to backtrack via xpath to get the "GROUP" id
        ) AS [T1]([x])
    CROSS APPLY
        [T1].[x].nodes(N'Row/*') AS [T2]([N])
    WHERE
        [T2].[N].value(N'local-name(.)', N'sysname') <> N'GROUP'
)
SELECT
    [InnerNodes].[xml] AS [*]
FROM
    (
        -- Probably preferable to use a table of numbers here
        SELECT DISTINCT
            [GROUP]
        FROM
            [cte_KVP]
    ) AS [Numbers]([Number])
CROSS APPLY
    (
        -- Generating the xml fragment specified by OP
        SELECT
            [cte_KVP].[Key]     AS [@name],
            [cte_KVP].[Value]   AS [data()]
        FROM
            [cte_KVP]
        WHERE
            [cte_KVP].[GROUP] = [Numbers].[Number]
        FOR XML PATH(N'field'), ROOT(N'doc'), TYPE
    ) AS [InnerNodes]([xml])
FOR XML PATH(N''), ROOT(N'add');
自找没趣 2024-12-11 04:31:53

它可能不像您想要的那么“自然”,如果您事先不知道列名称,则必须动态构建它,但这似乎会产生您想要的文档:

SELECT 
    CONVERT(XML, '<field name="id">' + RTRIM(id) + '</field>'
    + '<field name="name">' + name + '</field>')
FROM dbo.[table]
FOR XML PATH(N'doc'), ROOT(N'add');

这是一个动态的方法:

DECLARE
    @table NVARCHAR(512) = N'dbo.[table]',
    @sql   NVARCHAR(MAX) = N'';

SELECT @sql += '
    + ''<field name="' + name 
    + '">'' + CONVERT(NVARCHAR(MAX), ' 
    + QUOTENAME(name) + ') + ''</field>'''
    FROM sys.columns 
    WHERE object_id = OBJECT_ID(@table);

SET @sql = 'SELECT CONVERT(XML, ' + STUFF(@sql, 1, 4, '')
    + ') FROM ' + @table 
    + ' FOR XML PATH(N''doc''), ROOT(N''add'');';

PRINT @sql;
-- EXEC sp_executesql @sql;

It's probably not as "natural" as you want, and if you don't know the column names in advance, you'd have to build it dynamically, but this seems to produce the doc you're after:

SELECT 
    CONVERT(XML, '<field name="id">' + RTRIM(id) + '</field>'
    + '<field name="name">' + name + '</field>')
FROM dbo.[table]
FOR XML PATH(N'doc'), ROOT(N'add');

And here is a dynamic approach:

DECLARE
    @table NVARCHAR(512) = N'dbo.[table]',
    @sql   NVARCHAR(MAX) = N'';

SELECT @sql += '
    + ''<field name="' + name 
    + '">'' + CONVERT(NVARCHAR(MAX), ' 
    + QUOTENAME(name) + ') + ''</field>'''
    FROM sys.columns 
    WHERE object_id = OBJECT_ID(@table);

SET @sql = 'SELECT CONVERT(XML, ' + STUFF(@sql, 1, 4, '')
    + ') FROM ' + @table 
    + ' FOR XML PATH(N''doc''), ROOT(N''add'');';

PRINT @sql;
-- EXEC sp_executesql @sql;
許願樹丅啲祈禱 2024-12-11 04:31:53

我假设您正在使用 .NET 进行编程,因为您使用的是 SQL Server。如果您考虑过使用 SolrNet 客户端将文档加载到 Solr 服务器中?

I am assuming that you are programming in .NET since you are using SQL Server. If you are have you considered using the SolrNet client to load documents into your Solr server?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文