65.9K
CodeProject 正在变化。 阅读更多。
Home

月份名称排序

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.94/5 (6投票s)

2017年9月19日

CPOL

6分钟阅读

viewsIcon

17272

downloadIcon

152

在数据库表、文件名或字符串中按月名称排序

引言

当我创建一个Paradox数据库表来存储生日和纪念日时,我对按月名称排序产生了兴趣。由于我没有所有的年份信息,并且想显示月份名称,因此我包含了月份名称、日期、年份和月份编号列。最后一项是为了让我能够按月份编号、日期、姓氏和名字排序。

由于我只偶尔使用Paradox,所以我没有找到如何使用SQL来创建一个基于两个表的显示。当时,我认为如果数据库产品包含一个月份文本列类型,可以按正确的顺序对月份名称进行排序,那将是很好的。下面,我将展示多种方法来完成同样的事情,而无需包含月份名称列和月份文本列,也无需修改数据库产品。为了测试目的,我使用了SQLite的一个实现。

按月名称排序的另一个用途是排序文件名。您可以拥有以月份命名的文件,并且这些文件按任何顺序更新。因此,您不能依赖于按修改日期/时间排序。一个例子是书店的促销计划,作者在他们的书籍发布日期之后来到书店进行签名。他们的出现可能需要按任何顺序重新安排。

参考1是我关于此主题的原始文章。我决定更新这篇文章,因为我找到了并且想到了更好的方法来实现相同的目标。

参考2解释了如何在Excel 2007中按月名称排序一列。基本上,您选择自定义排序,然后选择月份名称的自定义列表。参考3提供了更多关于Excel自定义列表的信息。

参考4描述了SQLite中包含的SQL部分。

带有月份编号的数据库表

考虑以下数据库表

CREATE TABLE BirthdaysMonthNums (
  FirstName	TEXT,
  LastName	TEXT,   
  MonthNum	INTEGER,
  Day		INTEGER
);

如果您的数据库产品支持 DATENAME[5],则可以使用以下查询。在此查询中,加号是连接运算符。在SQLite中,连接运算符是 ||

SELECT FirstName, LastName, DATENAME (month, '2000-' + MonthNum + '-01'), _
Day FROM BirthdaysMonthNums ORDER BY MonthNum, Day, LastName, FirstName;

如果您的数据库产品不支持 DATENAME,则需要一个将月份编号映射到月份名称的表。

CREATE TABLE MonthNames (
  MonthNumber INTEGER,
  MonthName   TEXT
);

INSERT INTO MonthNames VALUES (1, 'January');
INSERT INTO MonthNames VALUES (2, 'February');
INSERT INTO MonthNames VALUES (3, 'March');
INSERT INTO MonthNames VALUES (4, 'April');
INSERT INTO MonthNames VALUES (5, 'May');
INSERT INTO MonthNames VALUES (6, 'June');
INSERT INTO MonthNames VALUES (7, 'July');
INSERT INTO MonthNames VALUES (8, 'August');
INSERT INTO MonthNames VALUES (9, 'September');
INSERT INTO MonthNames VALUES (10, 'October');
INSERT INTO MonthNames VALUES (11, 'November');
INSERT INTO MonthNames VALUES (12, 'December');

然后查询如下

SELECT FirstName, LastName, MonthName, Day FROM BirthdaysMonthNums, _
MonthNames WHERE MonthNum = MonthNumber ORDER BY MonthNumber, Day, _
LastName, FirstName;

带有月份名称的数据库表

将月份名称放入生日/纪念日表中可以使行更容易阅读,因为许多人更喜欢将日期视为具有月份名称。然而,缺点是使用更多空间来重复月份名称以及拼写错误月份名称的可能性。尽管如此,对包含月份名称的表进行排序是可能的。

考虑以下数据库表

CREATE TABLE BirthdaysMonthNames (
  FirstName	TEXT,
  LastName	TEXT,   
  Month	    TEXT,
  Day		INTEGER
);

如果您的数据库产品支持 DATEPART[6],则可以使用以下查询,该查询基于参考7。

SELECT * FROM BirthdaysMonthNames order by DATEPART _
(mm, CAST (Month + ' 1900' AS DATETIME)), Day, LastName, FirstName;

或者,您可以使用此查询,该查询基于参考8。

SELECT * FROM BirthdaysMonthNames ORDER BY MONTH ('1' + Month + '00'), _
Day, LastName, FirstName;

在此查询中,MONTH(日期)的效果与 DATEPART(月份,日期)[9] 相同,并且转换是隐式的。

如果您的数据库产品不支持 DATEPARTMONTH,则需要一个将月份名称映射到月份编号的表。

CREATE TABLE MonthOrder (
  MonthName   TEXT,
  MonthNumber INTEGER
);

INSERT INTO MonthOrder VALUES ('January', 1);
INSERT INTO MonthOrder VALUES ('Jan', 1);
INSERT INTO MonthOrder VALUES ('February', 2);
INSERT INTO MonthOrder VALUES ('Feb', 2);
INSERT INTO MonthOrder VALUES ('March', 3);
INSERT INTO MonthOrder VALUES ('Mar', 3);
INSERT INTO MonthOrder VALUES ('April', 4);
INSERT INTO MonthOrder VALUES ('Apr', 4);
INSERT INTO MonthOrder VALUES ('May', 5);
INSERT INTO MonthOrder VALUES ('June', 6);
INSERT INTO MonthOrder VALUES ('Jun', 6);
INSERT INTO MonthOrder VALUES ('July', 7);
INSERT INTO MonthOrder VALUES ('Jul', 7);
INSERT INTO MonthOrder VALUES ('August', 8);
INSERT INTO MonthOrder VALUES ('Aug', 8);
INSERT INTO MonthOrder VALUES ('September', 9);
INSERT INTO MonthOrder VALUES ('Sept', 9);
INSERT INTO MonthOrder VALUES ('Sep', 9);
INSERT INTO MonthOrder VALUES ('October', 10);
INSERT INTO MonthOrder VALUES ('Oct', 10);
INSERT INTO MonthOrder VALUES ('November', 11);
INSERT INTO MonthOrder VALUES ('Nov', 11);
INSERT INTO MonthOrder VALUES ('December', 12);
INSERT INTO MonthOrder VALUES ('Dec', 12);

此表包含缩写,但不包含这些缩写后的句点。

然后查询如下

SELECT FirstName, LastName, Month, Day FROM BirthdaysMonthNames, _
MonthOrder WHERE RTRIM (Month, '.') LIKE MonthName ORDER BY MonthNumber, _
Day, LastName, FirstName;

在此查询中,月份比较不区分大小写,并且会从月份 string 的右侧修剪句点。这使得在 BirthdaysMonthNames 中设置月份的灵活性有所提高。

文件名或字符串排序

在文件名或 string 排序中,您需要一种方法来识别月份名称。在英语中,March、April、May 和 June 可以是常见单词(march 和 may)或女性名字(April、May 和 June)。一种实现此目的的方法是用大括号将月份名称括起来。在 MonthNameOrdering 程序中,假设整个 string 都是月份名称。请参阅上图。

要使用此程序,请先加载或添加一组月份名称。可用的选项包括英文和法文的完整月份名称、缩写或两者兼有。这些是基于太阳历的。还包括犹太历的选项,这是一个可能有闰年的阴历[10]。与太阳历的闰年相比,阴历的闰年包含一个额外的月份。

中国农历也有闰年。然而,选择重复的月份是不规则的。它大约每32或33个月插入一次[11],或者大约每三年一次,其名称与前一个月相同[12]。

加载一个或多个月份名称集后,按“按字母顺序排序”按钮或“按月份顺序排序”按钮。在这两种情况下,重复的名称将被删除。

要按字母顺序排序,会将月份名称复制到STL向量中,然后通过调用 std::sort 进行排序。然后通过调用 std::unique,如果需要,再调用向量的erase成员函数来删除重复项。

要按月份顺序排序,会使用lambda函数作为第三个参数调用 std::sort。该函数使用月份名称和月份编号之间的STL映射。它可以简单地如下

[&](CString str1, CString str2) 
{return m_mapMonthNames[str1] < m_mapMonthNames[str2];}

然而,为了允许将同一月份的较长名称放在前面,此lambda函数变为如下

[&](CString str1, CString str2) {
	if (m_mapMonthNames[str1] == m_mapMonthNames[str2])
		return str2.GetLength() < str1.GetLength();
	else
		return m_mapMonthNames[str1] < m_mapMonthNames[str2];
}

m_mapMonthNames 在加载月份名称时定义,并允许缩写与相应的完整名称具有相同的月份索引。此外,设置此映射的函数允许混合使用太阳历和阴历。它通过将月份编号与整数高位中的级别编号进行按位或运算来实现。

m_mapMonthNames[strTrimmed] = nMonth | (nLevel << 16);

与前一个具有相同索引的月份名称以等号开头,然后从名称中删除。您可以将 (nLevel << 16) 的结果存储在一个在设置 m_mapMonthNames 的循环之外定义的变量中,这样就可以避免在循环中重复此计算。

参考文献

© . All rights reserved.