【问题标题】:Type II dimension joinsII 型维度连接
【发布时间】:2010-11-14 18:48:24
【问题描述】:

我在OLTP中有如下表查找表

CREATE TABLE TransactionState
(
    TransactionStateId INT IDENTITY (1, 1) NOT NULL,
    TransactionStateName VarChar (100)
)

当这进入我的 OLAP 时,我将结构更改如下:

CREATE TABLE TransactionState
(
    TransactionStateId INT NOT NULL, /* not an IDENTITY column in OLAP */
    TransactionStateName VarChar (100) NOT NULL,
    StartDateTime DateTime NOT NULL,
    EndDateTime NULL
)

我的问题是关于 TransactionStateId 列。随着时间的推移,我的 OLAP 中可能有重复的 TransactionStateId 值,但结合 StartDateTime 和 EndDateTime,它们将是唯一的。

我看到了 Type-2 维度的示例,其中添加了 OriginalTransactionStateId 并将传入的 TransactionStateId 映射到它,加上一个新的 TransactionStateId IDENTITY 字段成为 PK 并用于连接。

CREATE TABLE TransactionState
(
    TransactionStateId INT IDENTITY (1, 1) NOT NULL,
    OriginalTransactionStateId INT NOT NULL, /* not an IDENTITY column in OLAP */
    TransactionStateName VarChar (100) NOT NULL,
    StartDateTime DateTime NOT NULL,
    EndDateTime NULL
)

我应该选择 bachellorete #2 还是 bachellorete #3?

【问题讨论】:

    标签: sql sql-server data-warehouse dimensions type-2-dimension


    【解决方案1】:

    通过这句话:

    结合StartDateTimeEndDateTime,它们将是独一无二的。

    您的意思是它们永远不会重叠,或者它们满足数据库UNIQUE 约束?

    如果是前者,那么您可以在连接中使用StartDateTime,但请注意它可能效率低下,因为它将使用"<=" 条件而不是"="

    如果是后者,那就用假身份。

    数据库通常不允许对此查询使用有效的算法:

    SELECT  *
    FROM    TransactionState
    WHERE   @value BETWEEN StartDateTime AND EndDateTime
    

    ,除非你用 SPATIAL 数据做神秘的把戏。

    这就是为什么您必须在 JOIN 中使用此条件:

    SELECT  *
    FROM    factTable
    CROSS APPLY
            (
            SELECT  TOP 1 *
            FROM    TransactionState
            WHERE   StartDateTime <= factDateTime
            ORDER BY
                    StartDateTime DESC
            )
    

    ,这将剥夺优化器使用HASH JOIN 的可能性,这在许多情况下对此类查询最有效。

    有关此方法的更多详细信息,请参阅本文:

    重写查询以便它可以使用 HASH JOIN 会导致性能提升 600% 倍,但只有当您的日期时间具有一天或更低的精度(或者哈希表会变得非常大)时才有可能。

    由于您的时间组件已从您的StartDateTimeEndDateTime 中剥离,您可以像这样创建CTE

    WITH    cal AS
            (
            SELECT CAST('2009-01-01' AS DATE) AS cdate
            UNION ALL
            SELECT DATEADD(day, 1, cdate)
            FROM   cal
            WHERE  cdate <= '2009-03-01'
            ),
            state AS
            (
            SELECT  cdate, ts.*
            FROM    cal
            CROSS APPLY
                    (
                    SELECT  TOP 1 *
                    FROM    TransactionState
                    WHERE   StartDateTime <= cdate
                    ORDER BY
                            StartDateTime DESC
                    ) ts
            WHERE   ts.EndDateTime >= cdate
            )
    SELECT  *
    FROM    factTable
    JOIN    state
    ON      cdate = DATE(factDate)
    

    如果您的日期范围超过 100 日期,请调整 CTE 上的 MAXRECURSION 选项。

    【讨论】:

    • @Quassnoi:StartDateTime 和 EndDateTime 的时间部分都被去掉了——它们将相隔一天。
    • @Quassnio:感谢您提供代码示例。您对我最初关于两个单身汉的问题有何看法?
    • @Tapori:如果“单身汉”是指SCD 类型,那么Type 2 更好,因为您将拥有多个2 版本的每个州。通常认为查询速度较慢(出于我在帖子中描述的原因),但使用帖子中的技术可以获得不错的性能。
    • @Tapori: Type 2 是使用StartDateEndDate 的设计。带有OriginalTransactionStateId 的是Type 3
    【解决方案2】:

    请注意IDENTITY(1,1) 是在该列中自动生成值的声明。这与PRIMARY KEY 不同,PRIMARY KEY 是使列成为主键聚集索引的声明。这两个声明意味着不同的东西,如果你不说PRIMARY KEY,就会影响性能。

    【讨论】:

    • @David B:请原谅,我没有为该表发布整个 DDL。该代码发布了它OTTOMH。由于我的问题是关于数据仓库设计的,所以我专注于那部分。
    【解决方案3】:

    您也可以使用 SSIS 来加载 DW。在 slowly changing dimension (SCD) 转换中,您可以设置如何对待每个属性。如果选择了历史属性,则将类型 2 SCD 应用于整行,并且转换会处理细节。如果您喜欢start_dateend_datecurrent/expired 列,您还可以进行配置。

    这里要区分的是主键和业务(自然)键之间的区别。主键唯一标识表中的一行。业务键唯一标识一个业务对象/实体,它可以在维度表中重复。每次应用 SCD 2 时,都会插入一个新行,主键是新的,但业务键相同;然后旧行被标记为过期,而新行被标记为当前 - 或者适当地填充开始日期和结束日期字段。

    DW不应该暴露主键,所以从OLTP传入的数据包含业务键,而主键的分配由DW控制; IDENTITY int 适用于维度表中的 PK。

    很酷的是,SSIS 中的 SCD 转换可以解决这个问题。

    【讨论】:

      猜你喜欢
      • 2012-04-13
      • 2020-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多