如何克服Netezza缺乏独特的约束/参照完整性执行?

iha*_*nny 2 database data-integrity netezza

似乎缺乏对执行2个基本约束(唯一和外键)的支持,是失去许多工时调试和解决难题的原因.什么开始是一个简单,易于修复的问题(重复行/不一致的主要细节表)在我们的应用程序中甚至在我们的硬件中都没有被注意到,增长并引发复杂的边缘场景(例如,自我加入与重复可能导致通货膨胀和储存耗尽).

  • Netezza在我们的环境中有多种用途:生产,研究,qa和分期.当然,我们的ETL过程不能成熟,也无法验证所有这些场景中的所有约束.
  • 即使在生产中使用的最成熟的应用程序中,在ETL加载数据时也会验证数据,我们创建了一系列表,每个表都是其前任计算的结果.有时,数据完整性在此过程中被破坏,而不是在开始时(由于有缺陷的声明)

任何人都可以推荐一种方法/工具来避免这些麻烦吗?

Ker*_*err 5

我知道有一个公认的答案,但我想提供一种替代方法.对我目前的职位不熟悉,我并不了解仓库中主要关键声明背后的所有业务决策.我开发了日志记录类型方法来跟踪重复行删除工作.以下是此设计的主要功能:

  • 始终保持最新,解决DDL/DML的流动性问题
    • 新/删除表
    • 新/更新的主键
    • 新/更新/删除的行
  • 自我填充的历史
    • 随着时间的推移跟踪改进
    • 为各级趋势分析提供基础
  • 轻松查询目标表以用于研究目的
    • 没有自我加入HAVING子句或查找所需的键列
  • 目前仅处理主键
    • 可以轻松扩展以解决唯一约束(_V_RELATION_KEYDATA中的CONTYPE ='u')

以下是Netezza视角所需的一切.需要注意的是,您需要填补空白以创建动态SQL.

首先,我创建了一个跟踪所有重复记录的数据库,表和内部rowid的表.

CREATE TABLE
    NZ_DUPLICATE_PKS
    (
        DATABASE_NAME CHARACTER VARYING(128) NOT NULL
        ,TABLE_OWNER CHARACTER VARYING(128) NOT NULL
        ,TABLE_NAME CHARACTER VARYING(128) NOT NULL
        ,ROW_ID BIGINT NOT NULL
        ,CURRENT_RECORD_INDICATOR CHARACTER(1) NOT NULL
        ,CREATE_TIMESTAMP TIMESTAMP NOT NULL
        ,LAST_UPDATE_TIMESTAMP TIMESTAMP NOT NULL
    )
DISTRIBUTE ON
    (
        ROW_ID
    );
Run Code Online (Sandbox Code Playgroud)

注意:分配键上的YMMV和进入表的行数.我们Netezza应用程序中的行ID具有甚至足够的自然分布,它在基于野马的NPS 10050上很好地服务于我.

接下来,创建了此表的暂存版本:

CREATE TABLE
    STG_NZ_DUPLICATE_PKS
    (
        DATABASE_NAME CHARACTER VARYING(128)
        ,TABLE_OWNER CHARACTER VARYING(128)
        ,TABLE_NAME CHARACTER VARYING(128)
        ,ROW_ID BIGINT
        ,CURRENT_RECORD_INDICATOR CHARACTER(1)
        ,CREATE_TIMESTAMP TIMESTAMP
        ,LAST_UPDATE_TIMESTAMP TIMESTAMP
    )
DISTRIBUTE ON
    (
        ROW_ID
    );
Run Code Online (Sandbox Code Playgroud)

然后,我从系统视图创建了动态查询,以便为登台表提供种子.这是我开始的基本查询:

SELECT
    DATABASE
    ,OWNER
    ,RELATION
    ,CONSTRAINTNAME
    ,ATTNAME
FROM
    {YOUR_DATABASE_NAME}._V_RELATION_KEYDATA
WHERE
    CONTYPE = 'p'
    -- Exclude the duplicate tracking table
    AND RELATION != 'NZ_DUPLICATE_PKS'
ORDER BY
    DATABASE
    ,OWNER
    ,RELATION
    ,CONSTRAINTNAME
    ,CONSEQ
;
Run Code Online (Sandbox Code Playgroud)

现在我遍历基本查询以动态创建插入查询.我的商店使用的是DataStage,它的方法很深奥,不值得在这里阐述.

注意:这是循环和构造动态SQL所需的一些工作.可以使用无数种shell,Perl,Python等.使用带有两个列键的示例表,下面是构造用于插入登台表的内容:

INSERT
INTO
    STG_NZ_DUPLICATE_PKS
    (
        DATABASE_NAME
        ,TABLE_OWNER
        ,TABLE_NAME
        ,ROW_ID
        ,CURRENT_RECORD_INDICATOR
        ,CREATE_TIMESTAMP
        ,LAST_UPDATE_TIMESTAMP
    )
SELECT
    '{YOUR_DATABASE_NAME}' DATABASE_NAME
    ,'{YOUR_TABLE_OWNER}' TABLE_OWNER
    ,'{YOUR_TABLE_NAME}' TABLE_NAME
    ,DUPS.ROWID ROW_ID
    ,'Y' CURRENT_RECORD_INDICATOR
    ,CURRENT_TIMESTAMP CREATE_TIMESTAMP
    ,CURRENT_TIMESTAMP LAST_UPDATE_TIMESTAMP
FROM
    {YOUR_TABLE_NAME} DUPS
    INNER JOIN
        (
            SELECT
                {KEY_COLUMN_1}
                ,{KEY_COLUMN_2}
            FROM
                {YOUR_TABLE_NAME}
            GROUP BY
                {KEY_COLUMN_1}
                ,{KEY_COLUMN_2}
            HAVING
                COUNT(*) > 1
        )
        KEYS
        ON
            DUPS.{KEY_COLUMN_1} = KEYS.{KEY_COLUMN_1}
            AND DUPS.{KEY_COLUMN_2} = KEYS.{KEY_COLUMN_2};
Run Code Online (Sandbox Code Playgroud)

循环遍历所有表以对登台表进行播种后,然后运行一系列查询,将数据库,所有者,表名和行ID视为缓慢变化的维度.此查询结束日期目标表中的记录,这些记录在登台表中不存在:

UPDATE
    NZ_DUPLICATE_PKS
SET
    CURRENT_RECORD_INDICATOR = 'N'
    ,LAST_UPDATE_TIMESTAMP = CURRENT_TIMESTAMP
WHERE
    CURRENT_RECORD_INDICATOR = 'Y'
    AND
    (
        DATABASE_NAME
        ,TABLE_OWNER
        ,TABLE_NAME
        ,ROW_ID
    )
    NOT IN
    (
        SELECT
            DATABASE_NAME
            ,TABLE_OWNER
            ,TABLE_NAME
            ,ROW_ID
        FROM
            STG_NZ_DUPLICATE_PKS
    )
;
Run Code Online (Sandbox Code Playgroud)

最后,将最新记录插入目标表:

INSERT
INTO
    NZ_DUPLICATE_PKS
    (
        DATABASE_NAME
        ,TABLE_OWNER
        ,TABLE_NAME
        ,ROW_ID
        ,CURRENT_RECORD_INDICATOR
        ,CREATE_TIMESTAMP
        ,LAST_UPDATE_TIMESTAMP
    )
SELECT
    DATABASE_NAME
    ,TABLE_OWNER
    ,TABLE_NAME
    ,ROW_ID
    ,CURRENT_RECORD_INDICATOR
    ,CREATE_TIMESTAMP
    ,LAST_UPDATE_TIMESTAMP
FROM
    STG_NZ_DUPLICATE_PKS
WHERE
    (
        DATABASE_NAME
        ,TABLE_OWNER
        ,TABLE_NAME
        ,ROW_ID
    )
    NOT IN
    (
        SELECT
            DATABASE_NAME
            ,TABLE_OWNER
            ,TABLE_NAME
            ,ROW_ID
        FROM
            NZ_DUPLICATE_PKS
        WHERE
            CURRENT_RECORD_INDICATOR = 'Y'
    )
;
Run Code Online (Sandbox Code Playgroud)

注意:我们的环境不是必须使用仅插入模型.Netezza老将会熟悉这种思路.如果您的环境是仅插入的,请相应地调整策略.

一切就绪后,找到重复的行进行调查是很容易的:

SELECT
    *
FROM
    {YOUR_TABLE_NAME}
WHERE
    ROWID IN
    (
        SELECT
            ROW_ID
        FROM
            NZ_DUPLICATE_PKS
        WHERE
            CURRENT_RECORD_INDICATOR = 'Y'
            AND DATABASE_NAME = '{YOUR_DATABASE_NAME}'
            AND TABLE_OWNER = '{YOUR_OWNER_NAME}'
            AND TABLE_NAME = '{YOUR_TABLE_NAME}'
    );
Run Code Online (Sandbox Code Playgroud)

我喜欢这个,因为它对所有表都很简单和相同,无论主键声明有什么不同.

我也经常使用这个查询来查看表中当前的主键违规:

SELECT
    DATABASE_NAME
    ,TABLE_OWNER
    ,TABLE_NAME
    ,COUNT(*) QUANTITY
FROM
    NZ_DUPLICATE_PKS
WHERE
    CURRENT_RECORD_INDICATOR = 'Y'
GROUP BY
    1
    ,2
    ,3
ORDER BY
    1
    ,2
    ,3;
Run Code Online (Sandbox Code Playgroud)

这总结了一切.我希望有些人觉得它很有用.我们已经通过这种方法取得了很多进展.在这一点上,你可能想知道我为什么遇到这些麻烦.我讨厌允许PK违规进入我们的仓库,我希望采取全面的方法来消灭它们.上述过程每天都在我们的生产环境中运行几个月.我们有大约350个声明主键的表,大小从5行到大约2亿行事实@ 10Gb.对于Netezza来说,这是一个非常适度的支出.我们的野马NPS 10050整个过程不到10分钟.