在SQL Server中存储季度和年份的最佳方法是什么?

4
什么是在数据库中存储季度和年份的最佳方法?我有一个付款表,需要分配季度/年份,以便可以轻松地确定支付是哪个季度进行的。
我的想法有:
a)在每个付款中添加两个int列 b)添加另一张表,并添加可能的值,最多提前5年,并使用ID将该表与付款表连接。
还有其他选项吗?也许有更好或更容易维护的方法。这个数据库将与C#程序一起使用。
8个回答

4
如果您必须使用单独的年份和季度而不是日期(因为您似乎有特定的报告要求),我建议在“PAYMENT”表中使用tinyint表示季度,使用smallint表示年份并将它们存储在该表中。不要将其存储在另一个表中,这样做不好,因为:
  • 您必须确保已生成足够的年份/季度
  • 您必须连接并使用外键
如果您将数据与记录一起存储,它将有助于读取性能。您的表可能很小,但始终记住性能是一个好习惯。
为什么呢?让我们想象一下您需要获取:

所有在特定季度内付款 付款金额超过某个特定金额且客户是特定客户

在这种情况下,您需要对所有项目进行覆盖索引,但仍然无法解决问题,因为您的查询是针对特定季度而不是季度年份。然而,将数据存储在表中将有助于轻量级执行计划。

对于每个支付或表连接?数据库会很小(我认为少于1GB)。 - MadBoy
感谢您的建议和清晰的解释。一旦 Stack 允许,我会接受您的答案 :) - MadBoy

3
我一直使用日期时间值,其中1月/4月/7月/10月分别代表每个季度。这样可以简化计算季度的开始/结束日期:
  • 开始日期:使用datetime列本身。
  • 结束日期:dateadd(month,3,quarterColumn)
另一种选择是ISO 8601。以下是用于Internet协议的ISO 8601配置文件:RFC 3339(建议标准)。
2011年每个季度的ISO 8601表示如下:
  • 2011-01-01/P3M
  • 2011-04-01/P3M
  • 2011-07-01/P3M
  • 2011-10-01/P3M
上述内容通过开始日期和持续时间(在此情况下为3个月)来指定持续时间。
ISO 8601日期/时间格式的优点在于字符串是(A)易读的,(B)它们可以正确排序,(C)它们易于解析,(D)这是一个国际标准。有些人“扩展”了ISO 8601的周记号,其中一年的一周看起来像2011W32(2011年的第32周),以季度记号的形式。使用这个非官方的扩展,2011年的四个季度如下:2011Q1、2011Q2、2011Q3和2011Q4。

2
如何基于付款日期使用计算列?我宁愿这样做,而不是同时拥有可能会失去同步的日期和季度/年份。另一方面,我想你可能需要具备与日期不符的不同年份/季度的能力,在这种情况下,你需要将它们分开。但我至少认为应该考虑使用计算列,因为这似乎是确保完整性的最佳方式。

支付日期无法使用。通常员工会在季度结束后记录付款。我需要存储虚假日期或季度的最后一天来使用它。 - MadBoy
1
@MadBoy - 我认为你在考虑两个不同的日期。一个日期明显与季度有关(也许是收到日期?)。另一个日期与输入时间有关。它们应该是分开的。如果付款日期(我描述的第一个日期)被更改,那么季度也应随之更改。 - Thomas
不会有这样的更改。我们有客户需要每个季度或者12个季度左右收到发票。我需要能够区分付款是否已经完成,如果没有,则创建发票。如果是,则忽略。 - MadBoy
这个日期代表的实际事件是什么?发布日期?清除日期?接收日期?无论是什么事件,将其放入适当的季度桶中的规则应该是一个规则而不是数据,因为规则很容易改变。 - Thomas
员工将在第二季度为第一季度创建发票/创建付款。这意味着我会有一个日期,比如说‘2011-04-05’,然后让员工选择他想要为哪个季度创建发票。因此,我可以保存季度的最后一天,或者只是带有季度号和日期的tinyint。但是很可能有人“忘记”开票,那么就会再过一个季度才会发出。 - MadBoy
显示剩余4条评论

1

在数据库中存储季度和年份取决于您的付款数据如何组织。例如,有多少不同的付款值被插入。季度/年份范围是否会变化等。

一个好的技巧是通过创建一个带有“DateTime”字段的单独表来“定义”季度/年份范围。您不需要加入该表,只需在C#编程中计算出该范围是否落在特定的支付季度内即可。

例如:

Table 1: Payments
-----------------
paymentID (int)
paymentAmount (double(7,2))
paymentDateTime (DateTime)

Table 2: QuarterYear
--------------------
quarterYearID (int)
dateFrom (date)
dateTo (date)
quarter (tinyint)
description (varchar)

示例数据

paymentID | paymentAmount |   paymentDateTime
------------------------------------------------
    1     |    20.24      |  2011-04-18 08:14:20
    2     |    34.15      |  2011-04-19 07:42:15
    3     |    51.87      |  2011-04-20 13:04:22

quarterYearID |  dateFrom  |   dateTo   | quarter |  description
-----------------------------------------------------------------
      1       | 2011-01-01 | 2011-03-31 |    1    | first quarter
      2       | 2011-04-01 | 2011-06-30 |    2    | second quarter 
      3       | 2011-07-01 | 2011-09-31 |    3    | third quarter
      4       | 2011-10-01 | 2011-12-31 |    4    | forth quarter   

获取“第二季度”所有付款的示例查询 dateValue是从付款表动态提取的变量。C#将处理“dateValue”的值。

SELECT quarter FROM QuarterYear WHERE cast('dateValue' AS date) BETWEEN dateFrom AND dateTo;

Aliostad提出了一个不错的建议,如果你想用代码来定义季度。我的建议是如何在数据库中动态定义/编辑季度。没有必要连接表格。 - TehFoobar

1
对于这样简单的东西,我建议只使用两个 int 列,并在需要使用日期范围时使用 dateadd 来构建(关键的)日期。
另一个选择是只使用一个日期列,可以存储该季度的第一天,因此一年中的四个日期将为 1 月 1 日、4 月 1 日、7 月 1 日和 10 月 1 日。您可以使用 datepart Q 和 Y 轻松提取季度和年份信息。

谢谢。我会选择 tinyint 和 int。 - MadBoy

1

两个整数怎么样,一个表示年份,另一个表示季度(1-4)。这是你所说的选项“a”吗?

选项“b”也可以,但你必须记得每年或每隔一段时间维护表格。


是的,这正是我对选项a的想法 :-) - MadBoy

1

我同意使用两个整数。

如果您需要按年份和季度进行排序或筛选,我建议添加一个由这两列组成的索引。


1

你甚至可以使用一个 tinyint。它足以存储 YYQ 形式的数据,例如 111、112、113、114、121 等,可供几年使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接