创建 20 年的 DB2 日历 table,其中的列取决于原始日期

Create a DB2 Calendar table for 20 years with columns dependant on the original date

我正在尝试创建一个日历 table 20 年,从 2000 年到 2020 年不等。目的是每天有一行以及其他一些将使用基于日历日期的逻辑的列产生。这方面的一个例子是将一列作为日历日期 (2000-01-01),并将年份列从日历日期列 (2000) 中的值中读取年份。

table 的代码如下:

CREATE TABLE TEST.CALENDAR(
CALENDAR_DATE         DATE    NOT NULL,
CALENDAR_YEAR         INTEGER NOT NULL,
CALENDAR_MONTH_NUMBER INTEGER NOT NULL,
CALENDAR_MONTH_NAME   VARCHAR(100),
CALENDAR_DAY_OF_MONTH INTEGER NOT NULL,
CALENDAR_DAY_OF_WEEK  INTEGER NOT NULL,
CALENDAR_DAY_NAME     VARCHAR(100),
CALENDAR_YEAR_MONTH   INTEGER NOT NULL);

目前,我有一堆 insert 语句手动插入此 table 超过 20 年的行。我想用变量创建一个插入语句,这个插入语句将以每天的增量插入数据,直到开始日期变量不小于结束日期变量。

目前,我根本无法让它工作,更不用说为任何其他列包含任何逻辑了。

变量插入语句的代码:

declare startdate DATE, enddate DATEset startdate = '2000-01-01'
set enddate = DATEADD(yy,20,startdate)
while startdate < enddate
begin insert into TEST.CALENDAR (CALENDAR_DATE) select startdate
set startdate = DATEADD(dd,1,startdate) end

任何人都知道我如何让它工作吗?

您可以使用 DB2 递归查询和日期函数执行此操作:

考虑:

with cte (
    calendar_date,
    calendar_year,
    calendar_month_number,
    calendar_month_name,
    calendar_day_of_month,
    calendar_day_of_week,
    calendar_day_name
) as (
    select 
        calendar_date,
        year(calendar_date),
        month(calendar_date),
        monthname(calendar_date),
        dayofmonth(calendar_date),
        dayofweek(calendar_date),
        dayname(calendar_date)
    from (values(date('2000-01-01'))) as t(calendar_date)
    union all
    select 
        calendar_date + 1,
        year(calendar_date + 1),
        month(calendar_date + 1),
        monthname(calendar_date + 1),
        dayofmonth(calendar_date + 1),
        dayofweek(calendar_date + 1),
        dayname(calendar_date + 1)
    from cte where calendar_date < date('2021-01-01')
)
select * from cte

注意:我不清楚 CALENDAR_YEAR_MONTH 列是什么意思,所以我把它分开了。

Demo on DB Fiddle前10天:

CALENDAR_DATE | CALENDAR_YEAR | CALENDAR_MONTH_NUMBER | CALENDAR_MONTH_NAME | CALENDAR_DAY_OF_MONTH | CALENDAR_DAY_OF_WEEK | CALENDAR_DAY_NAME
------------: | ------------: | --------------------: | ------------------: | --------------------: | -------------------: | ----------------:
   2000-01-01 |          2000 |                     1 |             January |                     1 |                    7 |          Saturday
   2000-01-02 |          2000 |                     1 |             January |                     2 |                    1 |            Sunday
   2000-01-03 |          2000 |                     1 |             January |                     3 |                    2 |            Monday
   2000-01-04 |          2000 |                     1 |             January |                     4 |                    3 |           Tuesday
   2000-01-05 |          2000 |                     1 |             January |                     5 |                    4 |         Wednesday
   2000-01-06 |          2000 |                     1 |             January |                     6 |                    5 |          Thursday
   2000-01-07 |          2000 |                     1 |             January |                     7 |                    6 |            Friday
   2000-01-08 |          2000 |                     1 |             January |                     8 |                    7 |          Saturday
   2000-01-09 |          2000 |                     1 |             January |                     9 |                    1 |            Sunday
   2000-01-10 |          2000 |                     1 |             January |                    10 |                    2 |            Monday

问题 • 相关知识

Currently, I cannot get this to work at all let alone include any logic for any other columns.

嗯,这是有原因的:

  • 因为关系模型建立在一阶谓词演算(又名一阶逻辑)

    • 宇宙中没有什么不能根据 RM 定义并存储在关系数据库中(即符合 RM),方便检索,不受限制(包括复杂查询和DW)。
  • 因为SQLRM的数据sub-language,它是 Set-oriented

    • 在 Set-oriented SQL.
    • 中没有任何代码要求无法实现
  • DB2 是一个关系数据库引擎,具有真正的 Set-oriented 处理,使用 SQL。

看来你并没有意识到这一点。相反,您正试图:

  1. 定义low-level数据结构

    • 您认为您的编程需要,

    • 而不是RM和SQL,
      内要求的 定义数据,作为数据,除了数据什么都没有。

  2. 编写操作这些数据结构所需的代码,即:

    • (a) 程序(一次一行;WHILE 循环;CURSORS;类似的可憎行为),而不是 Set-oriented,和

    • (b) 因此代码即使不是不可能也很复杂。

    • 不说了,冬天慢如枫糖浆

    • 而不是使用可用的快得令人眼花缭乱的 Set-oriented 代码,这将是简单且 straight-forward。

    • 这个问题可能有点棘手,但是 table 和所需的代码不是。

问题 • 平台知识

An example of this would be having One column as the calendar date (2000-01-01) and the year column reading the year from the values within the calendar date column (2000)

这违反了两个原则,导致大量重复每行:

  1. 为数据使用正确的数据类型,就像 CALENDAR_DATE 一样。 只有.
    它是 DATE 数据类型
    使用 built-in DATE 数据类型和 DATE_PART(), DATEADD() 函数意味着 DB2 控制年份;月;天;等值
    并防止所有 DATE 错误,例如 29-Feb-201931-Sep-2019
    而不是您的代码,它可能有一个或多个错误。

    • 包含日期的 任何 部分的列必须存储为 DATE 数据类型(时间的任何部分存储为 TIME;日期和时间为 DATETIME;等)。
  2. 它打破了 Codd 的第一范式(不同于 ever-changing 假装者声称的“1NF”的疯狂)
    .
    每个域 [列,数据] 必须是 Atomic wrt 平台
    .
    DB2 完美地处理了 DATEDATE_PART(),所以没有任何借口。

  3. 所有下面的列是多余的,重复CALENDAR_DATE *在同一行`:

    CALENDAR_YEAR
    CALENDAR_MONTH_NUMBER
    CALENDAR_MONTH_NAME
    CALENDAR_DAY_OF_MONTH
    CALENDAR_DAY_OF_WEEK
    CALENDAR_DAY_NAME
    CALENDAR_YEAR_MONTH
    
    • 有点像买车(CALENDAR_DATE),开着它开,然后在它旁边走路(7个重复的DATE零件)。您需要了解平台; SQL,信任一点。

    • 不仅如此,你还会玩得不亦乐乎,努力把那些重复的列填出来而不出错。

    • 需要说明的是,你需要对DB2中所有的日期函数都比较了解,才能熟练使用。

    • 它们是重复的列,因为可以通过 DATE_PART() 轻松地 派生 值,例如:

    SELECT  DATE,
        Year       = DATE_PART( 'YEAR', DATE ),
        MonthNo    = DATE_PART( 'MONTH', DATE ),
        MonthName  = SUBSTR( ‘January  February March    April    May      June     July     August   SeptemberOctober  November December ‘, 
                     ( DATE_PART( 'MONTH', DATE ) - 1 ) * 9 + 1, 9 ),
        DayOfMonth = DATE_PART( 'DAY', DATE ),
        DayOfWeek  = DATE_PART( 'DOW', DATE ),
        DayName    = SUBSTR( ‘Sunday   Monday   Tuesday  WednesdayThursday Friday   Saturday',
                     ( DATE_PART( 'DOW', DATE ) - 1 ) * 9 + 1, 9 ),
        YearMonth  = DATE_PART( 'YEAR', DATE ) * 100 + DATE_PART( 'MONTH', DATE )
    FROM TEST.CALENDAR
    
    • 在 Sybase 中,我不必使用 SUBSTR(),因为我在 table 中有 MonthName 和 DayName 值,查询更简单。或者使用 CASE.
  4. 不要在每个 table 中的列前加上 table 名称。在 SQL 中引用特定 table 中的列以解决歧义,请使用:
    .
    TableName.ColumnName
    .
    与在 table 名称前加上用户名 TEST.CALENDAR 相同。

    • 完整规范如下,DB2 根据命令上下文提供相关默认值:
      .
      [SERVER.][Database.][Owner.][Table.]Column

    • 这条规则的原因是这样的。一个 table 中的列很可能与另一个 table 中的同一列相关,并且应该命名为相同的。这就是 Relational 的本质。如果您违反这条规则,它将阻碍您对 关系模型 和 SQL 及其数据 sub-language.

      [= 的逐步理解276=]

问题 • 数据知识

The aim is to have one row per day along with some other columns that will use logic based on the calendar date generated.

你到底为什么要这样做吗?

我们将关于宇宙的事实存储在关系数据库中。

  • 我们不需要存储non-facts,如:

    • Kyle's name isNOT"Fred"
    • CustomerCode "IBX" doesNOTexist
  • 一个non-fact只是一个存储事实的缺失

  • 如果Fred不存在Persontable,而你
    SELECT ... FROM Person WHERE Name = "Fred"
    您将获得一个空的结果集。
    理所应当。

你存储的是你想象的的网格,由
组成 20 年
* 365 天
* 任何 Key 是相关的 [例如。 CustomerCode, 等),
以行的形式。

  • 那只会保留数据库chock-full的空行,存储non-facts比如[eg.]CustomerCode XYZ has no event on each date for the未来 20 年。

  • 您想象的是结果集或视图,您可以在 GUI 应用程序中构建它。不是存储。

  • 仅存储事实,[例如] 每个客户的实际事件。

解决方案

现在是解决方案。

Let me assure you that I have implemented this structure, upon which fairly complex logic has been built, in quite a few databases.
- The problem is, educating the developers in order to get them to write correct SQL code.
- Your situation is that of a developer, trying to not only write non-simple code, but to define the structures upon which it depends.
- Two distinct and different sciences.

数据模型

首先,可视化数据模型,以便正确理解数据。

  • 我所有的数据模型都在 IDEF1X 中呈现,这是自 1993 年以来的关系数据库建模标准

  • 我的IDEF1X Introduction是初学者必读的

SQL DDL

仅仅因为你似乎在那个级别工作:

CREATE TABLE TEST.Customer (
    CustomerCode  CHAR(6)  NOT NULL,
    Name          CHAR(30) NOT NULL, 
    CONSTRAINT PK
        PRIMARY KEY ( CustomerCode )
    CONSTRAINT AK1
        UNIQUE ( Name )
    ...
    );

CREATE TABLE TEST.Event (
    CustomerCode  CHAR(6)  NOT NULL,
    Date          DATE     NOT NULL,
    Event         CHAR(30) NOT NULL,
    CONSTRAINT pk
        PRIMARY KEY ( CustomerCode, Date )
    CONSTRAINT Customer_Schedules_Event
        FOREIGN KEY         ( CustomerCode )
        REFERENCES Customer ( CustomerCode )
    ...
    );
  1. INSERT只有一个Customer保留一个Date
    一行 Non-facts未存储。

  2. SELECT ... WHERE CustomerCode = ... AND Date = ...
    如果客户在该日期预订了活动,将获得一行
    如果客户 NOT 在该日期

  3. 预订了活动,则将一无所获(空结果集)
  4. 如果需要存储重复出现的事件,比如未来20年的生日,使用SQL中的Projection来生成20条INSERT命令,也就是Set-oriented方法。

    • 如果您无法编写投影代码,那么只能编写一个 WHILE 循环,这是程序性的,每次执行一行。

有问题欢迎提问,越具体越好


如您所见,这个问题实际上是关于如何为事件设置日历,但我不会更改标题直到我确定这个答案就是你要找的。以及关于为关系数据库建模数据。我会添加标签。