如何存储不可变的、唯一的、有序的、列表?

Mal*_*lex 5 schema database-design sql-server

我有一个拥有数百万个实例的实体。每个实例都必须引用一个有序的项目列表。列表必须是唯一的,因此不会多次存储列表。但是一旦创建,列表和实体实例都是不可变的。实体实例远多于列表,并且数据库必须支持实体的快速插入

那么,什么是插入高效、健壮、存储不可变唯一有序列表的方式


编辑:列表项是简单的整数,典型长度约为 5 项。一长串的清单,比如 10 或 20 个项目是不太可能的,但可能。


编辑:到目前为止,我已经考虑过这些方法:

1)

lists表有这些列:<list_id> <order> <item>所以如果列表 #5 包含[10,20,30]表将包含的元素:

5   1   10
5   2   20
5   3   30
    
Run Code Online (Sandbox Code Playgroud)

实体表将有一个item_list_id引用该lists表的列(它不是外键,因为list_id它不是lists表中的唯一列-这可以通过添加另一个包含所有有效列的表来解决list_ids )。

  • 这个解决方案使插入有点棘手
  • 它还负责在应用程序上强制执行列表的唯一性,这不是很好。

2)

lists表有这些列:<list_id> <item1> <item2> <item3> ... <itemN>所以如果列表 #5 包含[10,20,30]表将包含的元素:

5   10   20   30
Run Code Online (Sandbox Code Playgroud)

实体表将有一item_list_id列引用该lists表。

  • 由于列表长度有限,此解决方案不太可靠(尽管这对我来说不是一个大问题,因为我的列表不太可能包含超过 10 或 20 个元素)
  • 这种方法查询起来非常糟糕(“查找出现特定项目的所有列表”必须指定每一列),并且是使用 ORM 映射的噩梦。
  • 插入新实体还不错,因为我的列表通常有 4-5 个项目长,所以我可能可以索引前几列。
  • 唯一性的强制执行仍然掌握在应用程序的手中。

3)

使用解决方案#1,但将无意义的替换为list_id序列化形式的列表中的散列(比如 SHA-1)。

  • 由于列表将具有唯一的哈希值,因此这种方式的唯一性得到了更严格的执行
  • 插入更简单、更快捷(?)
  • 数据完整性的执行仍然掌握在应用程序的手中。

Dan*_*her 1

您可能可以使用 XML 数据类型来存储列表。这样,您也可以保留顺序。尚未进行任何广泛的性能测试,但对于类型化 XML(架构)和列上的 XML 索引,这可能适合您。

至于插入性能,这种方法的一个优点是您只需向单个表中进行插入,这减少了许多死锁和争用问题的风险,特别是在事务中。

创建 XML 架构:

CREATE XML SCHEMA COLLECTION dbo.itemlist AS '
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="list">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="item" type="xsd:int" minOccurs="0" maxOccurs="unbounded"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>';
Run Code Online (Sandbox Code Playgroud)

创建具有类型化 XML 列的表:

CREATE TABLE dbo.instances (
    instance_id     int IDENTITY(1, 1) NOT NULL,
    list            xml(itemlist) NOT NULL,
    PRIMARY KEY CLUSTERED (instance_id)
);
Run Code Online (Sandbox Code Playgroud)

创建 XML 索引:

CREATE PRIMARY XML INDEX IX_instances_list
    ON dbo.instances (list);
Run Code Online (Sandbox Code Playgroud)

一些示例数据,大约一百万行左右:

INSERT INTO dbo.instances (list) VALUES ('<list><item>1</item><item>2</item><item>3</item></list>');

WHILE (@@ROWCOUNT<500000)
    INSERT INTO dbo.instances (list)
    SELECT '<list><item>'+CAST(CAST(1000*RAND(10+instance_id) AS int) AS varchar(10))+
          '</item><item>'+CAST(CAST(1000*RAND(20+instance_id) AS int) AS varchar(10))+
          '</item><item>'+CAST(CAST(1000*RAND(30+instance_id) AS int) AS varchar(10))+
          '</item></list>'
    FROM dbo.instances;
Run Code Online (Sandbox Code Playgroud)

以下是提取数据的方法:

SELECT i.instance_id,
       listitem.x.value('.', 'int') AS item
FROM dbo.instances AS i WITH (NOLOCK)
CROSS APPLY i.list.nodes('/list/item') AS listitem(x);
Run Code Online (Sandbox Code Playgroud)

...但是,除非提供子句,否则无法保证任何记录集的顺序ORDER BY,因此以下是如何将 a 添加ROW_NUMBER()到 XML 结果集:

SELECT i.instance_id,
       listitem.x.value('.', 'int') AS item,
       ROW_NUMBER() OVER (
           PARTITION BY i.instance_id
           ORDER BY listitem.x) AS item_order
FROM dbo.instances AS i WITH (NOLOCK)
CROSS APPLY i.list.nodes('/list/item') AS listitem(x);
Run Code Online (Sandbox Code Playgroud)