2011-09-13 57 views
4

我想要得到一個SQL select語句來生成符合Solr標準的XML。SQL選擇FOR XML到Solr文檔

給定一個表所示:

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或其他一些機制來匹配該模式?

回答

1

我假設你使用SQL Server進行.NET編程。如果您是否考慮使用SolrNet客戶端將文檔加載到Solr服務器中?

+0

由於佩奇,我已經看了幾個.NET庫,以幫助加載Solr的,但此刻我只是嘗試,並從試圖間接填充索引數據庫(有jdbc和域認證的一些問題) – STW

4

它可能不像您想要的那樣「自然」,並且如果您事先不知道列名稱,則必須動態構建它,但這似乎會產生您之後的文檔:

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; 
5

下面是使用構造方式略有不同。

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的意見*

這是在後阿龍在他的評論中引用描述的方法的概念證明激發活力且非高性能方法。 (它在更大的數據集執行可怕)

-- 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'); 
+0

我想指出,我不確定使用convert(xml,'').query('')生成內部節點的性能影響。 – etliens

+0

+1比我的方法更整齊。當然,如果你不想對列名進行硬編碼,構建起來會更麻煩一點。 –

+0

@Aaron - 完全同意。在附註中,我也希望sql server也支持計算節點名稱。 – etliens