SQL:从另一个表动态构建一个表

标签 sql sql-server xml

我的问题有点不寻常。

我有一个包含 3 列的表格 ( SQL Fiddle Link ):

ItemIdentifer  ItemClasses            RelevantItemDetails

Item1          Software              <xml document>
Item1          CRM                   <xml document>

现在,我需要做的是:

  1. 选择具有给定标识符的行。 (比如 Item1)

  2. 为具有给定标识符的每一行构建一个临时表:这样 Table_Name 就是该行的 ItemClass。 (因此,在这种情况下,我将有 2 个表——软件和 CRM。)

  3. 将 RelevantItemDetails 值 => 一个 xml 文本 => 转换为相应表的表条目(我知道这个 :))。

我已经学会了通过 openxml 页面做第 3 步 https://learn.microsoft.com/en-us/sql/t-sql/functions/openxml-transact-sql .我创建了一个从 xml 返回表的 sql 过程。现在,我只需要将它插入到我在步骤 2 中创建的临时表中(同时包含列名和值)。我迷失了。

我怀疑对于 1 和 2,我必须构建一个动态执行这些操作的过程。

任何帮助都会非常有帮助。

澄清一下:我没有得到两件事:

  1. 以 ItemClass 名称作为表名动态构建表。 (所以,这里有 2 个表用于 Item1)。

  2. 该表的列和行由 xml 文档(即另一个表)定义。

So, I just need to create tables with name defined by ItemClass and columns and row entries defined by the xml document (assume I have a table created from the xml, it too will be dynamically generated, but I know how to generate it).

我在此处阅读了动态创建表 (T-SQL How to create tables dynamically in stored procedures?),但列是固定的。在我的例子中,列是从另一个表派生的。而且我也必须插入值(同样,从列所在的同一个表派生)。

编辑:示例行。

ID  ItemName    ItemClass   Details
10   WebApp     Software   <root><row ID="10" ItemName="WebApp" ItemDescription="desc" DisplayID="4962" /></root>

编辑:最终表格示例。

TableName == Software

ID  ItemName  ItemDescription  DisplayID 
10  WebApp    "desc"            4962

最佳答案

@items 表类似于您当前的表。然后是为每个 ItemClass 生成脚本的步骤。

DECLARE @n int,  -- counter
        @i int = 0, -- counter
        @tableName nvarchar(255), -- stores name of the current table
        @query nvarchar(4000)  -- stores a query that creates table

-- creates table like yours
DECLARE @items TABLE (
    ID int,
    ItemName nvarchar(100),
    ItemClass nvarchar(100),
    Details xml
)
-- put some data in a table
INSERT INTO @items VALUES
(10, N'WebApp', N'Software', N'<root><row ID="10" ItemName="WebApp" ItemDescription="desc" DisplayID="4962" /></root>'),
(12, N'WebApp', N'Software', N'<root><row ID="12" ItemName="WebApp" ItemDescription="desc" DisplayID="5687" /></root>'),
(11, N'CRMapp', N'CRM', N'<root><row ID="11" ItemName="CRMapp" ItemDescription="desc" DisplayID="823678" /></root>')

-- that table will store data for script generating
DECLARE @tables TABLE (
    ID int,
    TableName nvarchar(100),
    ColumnName sysname,
    ColumnValue nvarchar(max)
)
--here we parse XML to get column names and data
INSERT INTO @tables
SELECT  i.ID,
        i.ItemClass as TableName,
        CAST(t.c.query('local-name(.)') AS nvarchar(255)) AS ColumnName,
        t.c.value('.', 'nvarchar(max)') ColumnValue
FROM @items i
CROSS APPLY Details.nodes('//@*') as t(c)
-- count tables
SELECT @n = COUNT(DISTINCT TableName)
FROM @tables

-- here we go!
WHILE @n > @i
BEGIN
    -- select some table 
    SELECT TOP 1 @tableName = TableName
    FROM @tables
    -- in this CTE we get column names in order
    ;WITH tables_ AS (
        SELECT  ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID ) as rn,
                ColumnName
        FROM @tables
        WHERE TableName = @tableName    
    )


    -- here we build the create part
    SELECT @query = N'CREATE TABLE ' + QUOTENAME(@tableName) + '('
    +
    STUFF((
        SELECT N','+ QUOTENAME(ColumnName) +' nvarchar(max)'
        FROM tables_
        GROUP BY rn,ColumnName
        ORDER BY rn
        FOR XML PATH('')),1,1,'')
    +');
INSERT INTO ' + QUOTENAME(@tableName) +' VALUES'
    -- here comes data
    ;WITH cte AS (
        SELECT  ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID ) as rn,
                ID,
                ColumnValue
        FROM @tables
        WHERE TableName = @tableName
    )

    SELECT @query = @query + STUFF((
    SELECT ',('''+ [1] + ''',''' +[2] + ''',''' +[3] + ''',''' +[4]+''')'
    FROM cte
    PIVOT (max (ColumnValue) for rn in ([1],[2],[3],[4])) as bb
    FOR XML PATH('')),1,1,'');
    -- print query
    PRINT @query
    ---- execute query (first runt this script to print!)
    --EXEC (@query)

    SET @i+=1
    SET @query = N''

    DELETE FROM @tables
    WHERE TableName = @tableName
END

这将生成(并执行)如下脚本:

CREATE TABLE [Software]([ID] nvarchar(max),[ItemName] nvarchar(max),[ItemDescription] nvarchar(max),[DisplayID] nvarchar(max));
INSERT INTO [Software] VALUES('10','WebApp','desc','4962'),('12','WebApp','desc','5687')

CREATE TABLE [CRM]([ID] nvarchar(max),[ItemName] nvarchar(max),[ItemDescription] nvarchar(max),[DisplayID] nvarchar(max));
INSERT INTO [CRM] VALUES('11','CRMapp','desc','823678')

您可以添加检查表是否已存在。以及一些为表列获取正确数据类型的逻辑。也许 XML 中可以有 4 个以上的属性。

关于SQL:从另一个表动态构建一个表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46693420/

相关文章:

c# - 您将如何比较两个 XML 文档?

php - php-PDO 中的 SQL 命令不起作用 - 不会引发错误

sql-server - 了解奇怪的数据库 key 设计

sql - 如何根据其他列中存在的值选择行

xml - Grails:将XML节点添加为插件的doWithWebDescriptor中的最后一个web.xml节点

c# - 如何反序列化 XML 文档

SQL:如何选择只购买了某种产品的用户?

sql - 从表中删除行会导致索引碎片?

asp.net - LINQ、存储过程、内联查询

sql - 大于SQL CASE语句