需要最高性能的 SQL 查询

4 sql-server

我正在存储带有在 DataSource 表中设置的字段的文档。文档写入数据库如下:

文档表(ID、DataSourceID) 文档字段(DocumentID、DataSourceField、FieldValue)

例如:

Document
100 | "Invoice"
101 | "Receipt"

Fields
100 | "Date" | 2011/12/01
100 | "Amount" | 1200,00
101 | "Date" | 2011/12/02
101 | "Warehouse" | "Central"
Run Code Online (Sandbox Code Playgroud)

文档有更多的字段。现在,问题是如何通过在字段上输入一些过滤器值来编写最快的查询来查找文档(即“2011/12/1”和“2011/12/31”之间的日期和金额 > 1000,00)?

这是我想出来的:

select * from Document, Fields 
where DocumentID = FieldDocumentID and DataSource = "Invoice"
and DocumentID in (select FieldDocumentID from Fields where Field = "Date"
and FieldValue between '2011/12/01' and '2011/12/31')
and DocumentID in (select FieldDocumentID from Fields where Field = "Amount"
and FieldValue >= 1000)
Run Code Online (Sandbox Code Playgroud)

我不确定这对数百万行的处理效果如何。我怎么能做得更好?

Con*_*lls 5

基于 EAV 的系统(例如 Agresso)具有 EAV 结构固有的查询性能问题。这些问题实际上基于三个潜在问题:

  • 查询的结构很繁琐,因此您的查询会增加复杂性。

  • 您无法根据任何属性对基表进行索引,这会导致查询计划中存在一些固有的低效率。

  • 您必须对大表​​进行多次连接。

缓解 EAV 性能问题的一些可能策略是:

  • 使用聚集索引将与给定父项相关的所有记录保持在物理上接近的位置会有所帮助。这最大限度地减少了 I/O。

  • 考虑使用元数据创建一个扁平化 EAV 结构的视图。每次视图更改时,您可能都必须重新定义视图,这不一定对性能有很大帮助,但查询起来会容易得多。尝试将视图创建为汇总而不是多路连接。

  • 任何不是严格用户定义的属性(例如交易日期、金额)都应该放在基表上。这些也可以被索引。

  • 首先解析子查询中最具选择性的谓词,然后加入子查询。

  • 如果您可以选择展平物理结构,即使它必须包含一系列字段,如“StringField1”、“StringField2”、“DateField1”、“MoneyField1”。无论如何,您的应用程序都必须保存配置数据,因此您只需映射到数据库中的一个字段即可。

    这听起来很笨拙,但查询效率要高得多,而且不必面对用户。您还可以使用元数据在命名字段的表上生成视图,以便数据的其他客户可以看到有意义的数据视图并查询它,而不必访问应用程序元数据。

    如果表中的列用完了,只需在数据库中添加更多列。字段映射系统可以随时查询系统数据字典,看看有什么'

在这些辩论的某个时刻,有人经常对 XML 列发表评论(“我知道,我将只使用 XML”)。套用 JWZ,现在你有两个问题。大约在 1998 年,他们在地狱中安装了另一个圈子,专门为那些使用 XML blob 字段玷污他们系统的人提供服务。

尽管它看起来并不圆滑和高科技,但在表上仅包含一组用户属性列是迄今为止最好的解决方案。它是最有效的,并且有很多方法可以使这种方法在数据库级别对用户更加友好。

编辑:这是 T-SQL 的一个片段,展示了如何创建一个扁平化 EAV 结构的视图。请注意,您必须根据应用程序的属性元数据为视图编写一个生成器,并在每次更改时重新创建它。

if object_id ('dbo.Foo') is not null
    drop table dbo.Foo
go

if object_id ('dbo.FooView') is not null
    drop view dbo.FooView
go 

create table dbo.Foo (
       FooID     int not null
      ,FooKey    varchar (20)
      ,FooVal    varchar (100)
)

insert Foo (FooID, FooKey, FooVal)
values (1, 'Date', '2011-11-11')

insert Foo (FooID, FooKey, FooVal)
values (1, 'Amount', '100')

insert Foo (FooID, FooKey, FooVal)
values (2, 'Date', '2012-12-12')

insert Foo (FooID, FooKey, FooVal)
values (2, 'Amount', '200')
go

create view dbo.FooView
    as
select *
  from (select FooID
              ,FooKey
              ,FooVal
          from dbo.Foo) keys
 pivot (Max (FooVal) 
        for FooKey in ([Date],[Amount])) pvt
go

select * from dbo.FooView
Run Code Online (Sandbox Code Playgroud)

这比在表上涉及多个自联接的解决方案更有效,并且在基本实体和属性类型键上使用聚集索引时效率更高。您可能还希望将类型转换放入视图中,以便您可以正确地对结果进行排序。