2023年2月

五行是指“金木水火土”,我国古代认为,宇宙是由金、木、水、火、土五种元素构成的,五行运动即相生相克的结果构成了大千世界。五行有正五行和纳音五行之分。
按天干地支的自身属性所定的五行为正五行,甲木、子水等;
(十大天干和十二地支,十天干:甲、乙、丙、丁、戊、己、庚、辛、壬、癸;十二地支:子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥;
天干地支纪年法首先是天干在前,地支在后,比如今年2005就为-乙酉年)

按干支结合生出的五行为纳音五行。纳音来源:六十甲子纳音,实即六十律逆相为宫之法。一律合五音,十二律即纳六十音。纳音的基本方法是:同类娶妻,隔八生子。这也是律吕相生的法则。干为天,支为地,音为人。

六十甲子纳音表

甲子乙丑海中金 丙寅丁卯炉中火 戊辰己巳大林木 庚午辛未路旁土 壬申癸酉剑锋金
甲戌乙亥山头火 丙子丁丑涧下水 戊寅己卯城头土 庚辰辛巳白腊金 壬午癸未杨柳木
甲申乙酉泉中水 丙戌丁亥屋上土 戊子己丑劈雳火 庚寅辛卯松柏木 壬辰癸巳长流水
甲午乙未沙中金 丙申丁酉山下火 戊戌己亥平地木 庚子辛丑壁上土 壬寅癸卯金箔金
甲辰乙巳佛灯火 丙午丁未天河水 戊申己酉大驿土 庚戌辛亥插环金 壬子癸丑桑枝木
甲寅乙卯大溪水 丙辰丁巳沙中土 戊午己未天上火 庚申辛酉石榴木 壬戌癸亥大海水

另外上面纳音表结合具体年份详细如下:

甲子年:(
1924

1984
)五行属海中金,屋上之鼠。


乙丑年:(
1925

1985
)五行属海中金,海内之牛。

丙寅年:(
1926

1986
)五行属炉中火,山林之虎。


丁卯年:(
1927

1987
)五行属炉中火,望月之兔。

戊辰年:(
1928

1988
)五行属大林木,清温之龙。


已巳年:(
1929

1989
)五行属大林木,福气之蛇。

庚午年:(
1930

1990
)五行属路旁土,堂里之马。


辛未年:(
1931

1991
)五行属路旁土,得禄之羊。

壬申年:(
1932

1992
)五行属剑锋金,清秀之猴。


癸酉年:(
1933

1993
)五行属剑锋金,楼宿之鸡。

甲戌年:(
1934

1994
)五行属山头火,守身之狗。


乙亥年:(
1935

1995
)五行属山头火,过往之猪。

丙子年:(
1936

1996
)五行属涧下水,田内之鼠。


丁丑年:(
1937

1997
)五行属涧下水,湖内之牛。

戊寅年:(
1938

1998
)五行属城头土,过山之虎。


巳卯年:(
1939

1999
)五行属城头土,山林之兔。

庚辰年:(
1940

2000
)五行属白腊金,怒性之龙。


辛巳年:(
1941

2001
)五行属白腊金,冬藏之蛇。

壬午年:(
1942

2002
)五行属杨柳木,军中之马。


癸未年:(
1943

2003
)五行属杨柳木,群内之羊。

甲申年:(
1944

2004
)五行属井泉水,过树之猴。


乙酉年:(
1945

2005
)五行属井泉水,唱午之鸡。

丙戌年:(
1936

2006
)五行属屋上圭,自眠之狗。


丁亥年:(
1947

2007
)五行属屋上圭,过山之猪。

戌子年:(
1948

2008
)五行属霹雷火,仓内之鼠。


巳丑年:(
1949

2009
)五行属霹雷火,栏内之牛。

庚寅年:(
1950

2010
)五行属松柏木,出山之虎。


辛卯年:(
1951

2011
)五行属松柏木,蟾窟之兔。

壬辰年:(
1952

2012
)五行属长流水,行雨之龙。


癸巳年:(
1953

2003
)五行属长流水,草中之蛇。

甲午年:(
1954

2004
)五行属沙中金,云中之马。


乙未年:(
1955

2015
)五行属沙中金,敬重之羊。

丙申年:(
1956

2016
)五行属山下火,山上之猴。


丁酉年:(
1957

2017
)五行属山下火,独立之鸡。

戊戌年:(
1958

2018
)五行属平地木,进山之狗。


已亥年:(
1959

2019
)五行属平地木,道院之猪。

庚子年:(
1960

2020
)五行属壁上土,梁上之鼠。


辛丑年:(
1961
年)


五行属壁上土,路途之牛。

壬寅年:(
1962
年)


五行属金泊金,过林之虎。

癸卯年:(
1963
年)


五行属金泊金,山林之兔。

甲辰年:(
1964
年)


五行属覆灯火,伏潭之龙。

乙巳年:(
1965
年)


五行属覆灯火,出穴只蛇。

丙午年:(
1966
年)


五行属天河水,行路之马。

丁未年:(
1967
年)


五行属天河水,失群之羊。

戊申年:(
1968
年)


五行属大泽土,独立之猴。

已酉年:(
1969
年)


五行属大泽土,报晓之鸡。

庚戌年:(
1970
年)


五行属钗钏金,寺观之狗。

辛亥年:(
1971
年)


五行属钗钏金,圈养之猪。

壬子年:(
1972
年)


五行属桑柘木,山上之鼠。

癸丑年:(
1973
年)


五行属桑柘木,栏外之牛。

甲寅年:(
1974
年)


五行属大溪水,立定只虎。

乙卯年:(
1975
年)


五行属大溪水,得道之兔。

丙辰年:(
1976
年)


五行属沙中土,天上之龙。

丁已年:(
1977
年)


五行属沙中土,塘内之蛇。

戊午年:(
1978
年)


五行属天上火,厩内之马。

已未年:(
1979
年)


五行属天上火,草野之羊。

庚申年:(
1980
年)


五行属石榴木,食果之猴。

辛酉年:(
1981
年)


五行属石榴木,笼藏之鸡。

壬戌年:(
1982
年)


五行属大海水,顾家之犬。

癸亥年:(
1983
年)


五行属大海水,林下之猪。

六十甲子納音之說,術家多不能曉。原其所以得名,皆從五音所生,有條不紊,端如貫珠。蓋甲子為首,而五音始於宮,宮土生金,故甲子為金,而乙丑以陰從陽。商金生水,故丙子為水,而丁丑從之。角木生火,故戊子為火。徵火生土,故庚子為土。羽水生木,故壬子為木。而己丑、辛丑、癸丑各從之。至於甲寅,則納音起於商,商金生水,故甲寅為水。角木生火,故丙寅為火。徵火生土,故戊寅為土。羽水生木,故庚寅為木。宮土生金,故壬寅為金。而五卯各從之。至甲辰,則納音起於角,角木生火,故甲辰為火。徵火生土,故丙辰為土。羽水生木,故戊辰為木。宮土生金,故庚辰為金。商金生水,故壬辰為水。而五巳各從之。宮、商、角既然,惟徵、羽不得居首。於是甲午復如甲子,甲申如甲寅,甲戌如甲辰,而五未、五酉、五亥,亦各從其類。


我们知道,六合一共有49个号码(1~49),由于五行号码是每年都会变化的,对应到六合里面, 如何获得该年的五行(金木水火土)对应的49个号码呢?

如2010年六合中的五行号码如下:

金:10,11,18,19,26,27,40,41,48,49,
木:01,08,09,22,23,30,31,38,39,
水:06,07,14,15,28,29,36,37,44,45,
火:02,03,16,17,24,25,32,33,46,47,
土:04,05,12,13,20,21,34,35,42,43,

在介绍使用C#来自动生成每年的五行号码前,我们先来看看使用易语言的实现逻辑。

.版本 2
.支持库 CnCalendar

.计次循环首 (49, 计次)
.如果真 (六十甲子纳音数组 [农历日期框1.农历年 - 1922 - 计次] = “金”)
金行文本 = 金行文本 + 取文本右边 (“0” + 到文本 (计次), 2) + “,”
.如果真结束
.如果真 (六十甲子纳音数组 [农历日期框1.农历年 - 1922 - 计次] = “木”)
木行文本 = 木行文本 + 取文本右边 (“0” + 到文本 (计次), 2) + “,”
.如果真结束
.如果真 (六十甲子纳音数组 [农历日期框1.农历年 - 1922 - 计次] = “水”)
水行文本 = 水行文本 + 取文本右边 (“0” + 到文本 (计次), 2) + “,”
.如果真结束
.如果真 (六十甲子纳音数组 [农历日期框1.农历年 - 1922 - 计次] = “火”)
火行文本 = 火行文本 + 取文本右边 (“0” + 到文本 (计次), 2) + “,”
.如果真结束
.如果真 (六十甲子纳音数组 [农历日期框1.农历年 - 1922 - 计次] = “土”)
土行文本 = 土行文本 + 取文本右边 (“0” + 到文本 (计次), 2) + “,”
.如果真结束

.计次循环尾 ()

C#生成逻辑中,我们首先生成一个六十甲子字符列表,并能根据六十甲子字符串,从六十甲子纳音表中获取主五行,代码如下所示:



代码


///

<summary>


///
获取六十甲子字符列表

///

</summary>


///

<returns></returns>



private
List
<
string
>
GetJiazhi()
{

string
str
=

@"
甲子 乙丑 丙寅 丁卯 戊辰 己巳 庚午 辛未 壬申 癸酉
甲戌 乙亥 丙子 丁丑 戊寅 己卯 庚辰 辛巳 壬午 癸未
甲申 乙酉 丙戌 丁亥 戊子 己丑 庚寅 辛卯 壬辰 癸巳
甲午 乙未 丙申 丁酉 戊戌 己亥 庚子 辛丑 壬寅 癸卯
甲辰 乙巳 丙午 丁未 戊申 己酉 庚戌 辛亥 壬子 癸丑
甲寅 乙卯 丙辰 丁巳 戊午 己未 庚申 辛酉 壬戌 癸亥

"
;
List

<
string
>
list
=

new
List
<
string
>
();

foreach
(
string
item
in
str.Split(
'

'
))
{

if
(
!
string
.IsNullOrEmpty(item))
{
list.Add(item.Replace(

"
\r\n
"
,
""
));
}
}

return
list;
}


///

<summary>


///
根据甲子获取纳音

///

</summary>


///

<param name="jiazhi"></param>


///

<returns></returns>



private

string
GetNayin(
string
jiazhi)
{

string
str
=

@"
甲子乙丑海中金 丙寅丁卯炉中火 戊辰己巳大林木 庚午辛未路旁土 壬申癸酉剑锋金
甲戌乙亥山头火 丙子丁丑涧下水 戊寅己卯城头土 庚辰辛巳白腊金 壬午癸未杨柳木
甲申乙酉泉中水 丙戌丁亥屋上土 戊子己丑劈雳火 庚寅辛卯松柏木 壬辰癸巳长流水
甲午乙未沙中金 丙申丁酉山下火 戊戌己亥平地木 庚子辛丑壁上土 壬寅癸卯金箔金
甲辰乙巳佛灯火 丙午丁未天河水 戊申己酉大驿土 庚戌辛亥插环金 壬子癸丑桑枝木
甲寅乙卯大溪水 丙辰丁巳沙中土 戊午己未天上火 庚申辛酉石榴木 壬戌癸亥大海水

"
;

int
iStart
=
str.IndexOf(jiazhi);

string
leftStr
=
str.Substring(iStart);


int
iSpace
=
leftStr.IndexOf(
'

'
);
leftStr

=
leftStr.Substring(
0
, iSpace);

leftStr

=
leftStr.Substring(leftStr.Length
-
1
,
1
);

return
leftStr;
}

继而我们把六十甲子列表、五行列表(金木水火土)、以及根据每一个六十甲子字符得到的五行列表放到变量中待用。如下所示



代码


//
甲子列表


List
<
string
>
jiazhiList
=
GetJiazhi();

//
纳音列表


List
<
string
>
nayinList
=

new
List
<
string
>
();


//
初始化五行数字字符串列表


Dictionary
<
string
,
string
>
wuhanStrList
=

new
Dictionary
<
string
,
string
>
() { };
wuhanStrList.Add(

"

"
,
""
);
wuhanStrList.Add(

"

"
,
""
);
wuhanStrList.Add(

"

"
,
""
);
wuhanStrList.Add(

"

"
,
""
);
wuhanStrList.Add(

"

"
,
""
);
for (int i = 1; i <= 60; i++)
{
string jiazhi = jiazhiList[i - 1];
string nayin = GetNayin(jiazhi);
nayinList.Add(nayin);
//this.textBox1.AppendText(nayin + Environment.NewLine);
}

最后我们根据1~49号码和当年的农历年数字,根据规则生成各五行的数字字符串,放到对应的五行数字字符串列表中,如下所示



代码

ChineseLunisolarCalendar chineseDate
=

new
ChineseLunisolarCalendar();

for
(
int
i
=

1
; i
<=

49
; i
++
)
{

int
currentYear
=
chineseDate.GetYear(
this
.dateTimePicker1.Value);

int
index
=
currentYear
-

1922

-
i
-

1
;

string
itemName
=
nayinList[index
%

60
];

if
(itemName
==

"

"
)
{
wuhanStrList[

"

"
]
+=
i.ToString(
"
D2
"
)
+

"
,
"
;
}

else

if
(itemName
==

"

"
)
{
wuhanStrList[

"

"
]
+=
i.ToString(
"
D2
"
)
+

"
,
"
;
}

else

if
(itemName
==

"

"
)
{
wuhanStrList[

"

"
]
+=
i.ToString(
"
D2
"
)
+

"
,
"
;
}

else

if
(itemName
==

"

"
)
{
wuhanStrList[

"

"
]
+=
i.ToString(
"
D2
"
)
+

"
,
"
;
}

else

if
(itemName
==

"

"
)
{
wuhanStrList[

"

"
]
+=
i.ToString(
"
D2
"
)
+

"
,
"
;
}
}


foreach
(
string
key
in
wuhanStrList.Keys)
{

this
.textBox1.AppendText(
string
.Format(
"
{0}:{1}\r\n
"
, key, wuhanStrList[key]));
}

最后给出一个实例截图(
程序文件下载地址
):

数据仓库(Data Warehouse)是一个面向主题的(Subject Oriented)、集成的(Integrate)、相对稳定的(Non-Volatile)、反映历史变化(Time Variant)的数据集合,用于支持管理决策。

对于数据仓库的概念我们可以从两个层次予以理解,首先,数据仓库用于支持决策,面向分析型数据处理,它不同于企业现有的操作型数据库;其次,数据仓库是对多个异构的数据源有效集成,集成后按照主题进行了重组,并包含历史数据,而且存放在数据仓库中的数据一般不再修改。

数据仓库与数据库的区别
数据仓库和数据库的主要区别如下:
1、 数据库是面向事务的设计,数据仓库是面向主题设计的。(后面我们会详细介绍数据仓库面向主题的特点)
2、 数据库一般存储在线交易数据,数据仓库存储的一般是历史数据。
3、 数据库设计是尽量避免冗余,一般采用符合范式的规则来设计,数据仓库在设计是有意引入冗余,采用反范式的方式来设计。
4、 数据库是为捕获数据而设计,数据仓库是为分析数据而设计,它的两个基本的元素是维表和事实表。(维是看问题的角度,比如时间,部门,维表放的就是这些东西的定义,事实表里放着要查询的数据,同时有维的ID)(后面会详细介绍)

粒度

粒度问题是设计数据仓库的一个重要方面。
粒度是指数据仓库的数据单位中保存数据的细化或综合程度的级别。
细化程度越高,粒度级就越小;相反,细化越低,粒度级就越大。
在数据仓库环境中粒度之所以是主要的设计问题,是因为它影响存放在数据仓库中的数据量的大小,同时影响数据仓库所能回答的查询类型。

元数据
元数据是关于数据的数据。
元数据的重要性在于:DSS分析人员在使用数据仓库进行决策分析时,需要得到尽可能多的帮助,而元数据恰好能很好地帮助他们。
元数据在数据仓库的上层,并且记录数据仓库中对象的位置。
典型的元数据主要记录:
1、 数据仓库表的结构
2、 数据仓库表的属性
3、 数据仓库的源数据(记录系统)
4、 从记录系统到数据仓库的映射
5、 数据模型的规格说明
6、 抽取数据的历史记录(日志)
7、 访问数据的公用例行程序

数据仓库体系结构
企业数据仓库的建设,是以现有企业业务系统和大量业务数据的积累为基础。
数据仓库不是静态的概念,只有把信息及时交给需要这些信息的使用者,供他们做出改善其业务经营的决策,信息才能发挥作用,信息才有意义。
而把信息加以整理归纳和重组,并及时提供给相应的管理决策人员,是数据仓库的根本任务。
因此,从产业界的角度看,数据仓库建设是一个工程,是一个过程。
整个数据仓库系统是一个包含四个层次的体系结构,具体如下所示:
 数据源:是数据仓库系统的基础,是整个系统的数据源泉。通常包括企业内部信息和外部信息。内部信息包括存放于关系型数据库RDBMS中的各种业务处理数据和各类文档数据。外部信息包括各类法律法规、市场信息和竞争对手的信息等等;
目前,我们的数据仓库的数据源主要是内部信息,也就是来源于各个信息系统下的关系型数据库。
 数据的存储与管理:是整个数据仓库系统的核心。数据仓库的真正关键是数据的存储和管理。针对现有各业务系统的数据,进行抽取、清理,并有效集成,按照主题进行组织。装载入数据仓库。数据仓库按照数据的覆盖范围可以分为企业级数据仓库和部门级数据仓库(通常称为数据集市)。
目前,我们的数据仓库主要是企业级数据仓库,并没有部门级数据仓库,也就是所谓的数据集市。
 OLAP服务器:对分析需要的数据进行有效集成,按多维模型予以组织,以便进行多角度、多层次的分析,并发现趋势。其具体实现可以分为:ROLAP、MOLAP和HOLAP。ROLAP基本数据和聚合数据均存放在RDBMS之中;MOLAP基本数据和聚合数据均存放于多维数据库中;HOLAP基本数据存放于RDBMS之中,聚合数据存放于多维数据库中。
 前端工具:主要包括各种报表工具、查询工具、数据分析工具、数据挖掘工具以及各种基于数据仓库或数据集市的应用开发工具。其中数据分析工具主要针对OLAP服务器,报表工具、数据挖掘工具主要针对数据仓库。

由于一直基于Oracle数据库上做开发,因此常常会需要把大量的Excel数据导入到Oracle数据库中,其实如果从事SqlServer数据库的开发,那么思路也是一样的,本文主要介绍如何导入Excel数据进入Oracle数据库的内容。

一般我们拿到的Excel数据,都会有一个表头说明,然后下面是一连串的数据内容,如下图所示:

而Oracle中数据库一般为英文名称,中文名称就需要转义,为了方便导入,我把中文名称对照数据库的字段,把表头修改为对应的字段名称,如果没有数据库对应的字段,那么删除Excel的无用列即可,如下所示。

首先我们在导入Excel的例子中加载显示要导入的数据,一个是为了直观,第二个也是为了检查数据的有效性,避免出错,界面如下所示:

在介绍导入操作前,我们先要分析下数据,否则就很容易出现错误的语句,一般日期的格式、数字的格式就要特别注意,文本格式一般看是否超出字段的长度,一般成功导入前都会发生好多次的错误问题,解决了这些格式的问题,基本上就OK了。如下面日期和数字的格式问题,就必须注意转换为对应的内容格式:


下面介绍具体的显示数据和导入数据的操作代码:

显示Excel数据的代码如下所示:


代码


private

string
connectionStringFormat
=

"
Provider = Microsoft.Jet.OLEDB.4.0 ; Data Source = '{0}';Extended Properties=Excel 8.0
"
;

private
DataSet myDs
=

new
DataSet();


private

void
btnViewData_Click(
object
sender, EventArgs e)
{

if
(
this
.txtFilePath.Text
==

""
)
{
MessageUtil.ShowTips(

"
请选择指定的Excel文件
"
);

return
;
}


string
connectString
=

string
.Format(connectionStringFormat,
this
.txtFilePath.Text);

try

{
myDs.Tables.Clear();
myDs.Clear();
OleDbConnection cnnxls

=

new
OleDbConnection(connectString);
OleDbDataAdapter myDa

=

new
OleDbDataAdapter(
"
select * from [Sheet1$]
"
, cnnxls);
myDa.Fill(myDs,

"
c
"
);

dataGrid1.DataSource

=
myDs.Tables[
0
];
}

catch
(Exception ex)
{
MessageBox.Show(ex.Message);
}
}

导入操作的代码如下所示(由于数据格式需要验证,以及需要判断数据库是否存在指定关键字的记录,如果存在,那么更新,否则插入新的记录,如果仅仅是第一次导入,操作代码可以更为精简一些):


代码


private

void
btnSaveData_Click(
object
sender, EventArgs e)
{

if
(
this
.txtFilePath.Text
==

""
)
{
MessageUtil.ShowTips(

"
请选择指定的Excel文件
"
);

return
;
}


if
(MessageUtil.ShowYesNoAndWarning(
"
该操作将把数据导入到系统的用户数据库中,您确定是否继续?
"
)
==
DialogResult.Yes)
{
InsertData();
}
}


private

bool
CheckIsDate(
string
columnName)
{

string
str
=

"
,PREPARE_DATE,COPY_DATE,COPY_VALIDITY,BUSINESS_VALIDITY,OPENING_APPROVAL_DATE,OPENING_DATE,EDITTIME,LICENSE_DATE,LICENSE_VALIDITY,TEMP_OPENING_DATE,LICENSE_START_DATE,ADDTIME,EDITTIME,
"
;

return
str.Contains(
"
,
"

+
columnName.ToUpper()
+

"
,
"
);
}


private

bool
CheckIsNumeric(
string
columnName)
{

string
str
=

"
,FIXED_CAPITAL,REG_CAPITAL,MARGIN,PARK_AREA,PARK_SPACE_NUMBER,
"
;

return
str.Contains(
"
,
"

+
columnName.ToUpper()
+

"
,
"
);
}


private

void
InsertData()
{

int
intOk
=

0
;

int
intFail
=

0
;


if
(myDs
!=

null

&&
myDs.Tables[
0
].Rows.Count
>

0
)
{

string
accessConnectString
=
config.GetConnectionString(
"
DataAccess
"
);
OracleConnection conn

=

new
OracleConnection(accessConnectString);
conn.Open();
OracleCommand com

=

null
;


#region
组装字段列表


string
insertColumnString
=

"
ID,
"
;
DataTable dt

=
myDs.Tables[
0
];

int
k
=

0
;

foreach
(DataColumn col
in
dt.Columns)
{
insertColumnString

+=

string
.Format(
"
{0},
"
, col.ColumnName);
}
insertColumnString

=
insertColumnString.Trim(
'
,
'
);


#endregion



try

{

foreach
(DataRow dr
in
dt.Rows)
{

if
(dr[
0
].ToString()
==

""
)
{

continue
;
}


#region
组装Sql语句


string
insertValueString
=

"
SEQ_TBPARK_ENTERPRISE.Nextval,
"
;

string
updateValueString
=

""
;

string
COMPANY_CODE
=
dr[
"
COMPANY_CODE
"
].ToString().Replace(
"
<空>
"
,
""
);


#region
拼接Sql字符串



for
(
int
i
=

0
; i
<
dt.Columns.Count; i
++
)
{

string
originalValue
=
dr[i].ToString().Replace(
"
<空>
"
,
""
);

//
if (!CheckIsDate(dt.Rows[0][i].ToString()))



if
(
!
CheckIsDate(dt.Columns[i].ColumnName))
{

if
(
!
string
.IsNullOrEmpty(originalValue))
{

if
(CheckIsNumeric(dt.Columns[i].ColumnName))
{
insertValueString

+=

string
.Format(
"
'{0}',
"
, Convert.ToDecimal(originalValue));
updateValueString

+=

string
.Format(
"
{0}='{1}',
"
, dt.Columns[i].ColumnName, Convert.ToDecimal(originalValue));
}

else

{
insertValueString

+=

string
.Format(
"
'{0}',
"
, originalValue);
updateValueString

+=

string
.Format(
"
{0}='{1}',
"
, dt.Columns[i].ColumnName, originalValue);
}
}

else

{
insertValueString

+=

string
.Format(
"
NULL,
"
);
updateValueString

+=

string
.Format(
"
{0}=NULL,
"
, dt.Columns[i].ColumnName);
}
}

else

{

if
(
!
string
.IsNullOrEmpty(originalValue))
{
insertValueString

+=

string
.Format(
"
to_date('{0}','yyyy-mm-dd'),
"
, Convert.ToDateTime(originalValue).ToString(
"
yyyy-MM-dd
"
));
updateValueString

+=

string
.Format(
"
{0}=to_date('{1}','yyyy-mm-dd'),
"
, dt.Columns[i].ColumnName, Convert.ToDateTime(originalValue).ToString(
"
yyyy-MM-dd
"
));
}

else

{
insertValueString

+=

string
.Format(
"
NULL,
"
);
updateValueString

+=

string
.Format(
"
{0}=NULL,
"
, dt.Columns[i].ColumnName);
}
}
}
insertValueString

=
insertValueString.Trim(
'
,
'
);
updateValueString

=
updateValueString.Trim(
'
,
'
);

#endregion



string
insertSql
=

string
.Format(
@"
INSERT INTO tbpark_enterprise ({0}) VALUES({1})
"
, insertColumnString, insertValueString);

string
updateSql
=

string
.Format(
"
Update tbpark_enterprise set {0} Where COMPANY_CODE='{1}'
"
, updateValueString, COMPANY_CODE);

string
checkExistSql
=

string
.Format(
"
Select count(*) from tbpark_enterprise where COMPANY_CODE='{0}'
"
, COMPANY_CODE);

#endregion



#region
写入数据


try

{
com

=

new
OracleCommand();
com.Connection

=
conn;
com.CommandText

=
checkExistSql;

object
objCount
=
com.ExecuteScalar();


bool
succeed
=

false
;

bool
exist
=
Convert.ToInt32(objCount)
>

0
;

if
(exist)
{

//
需要更新

//
WriteString(updateSql);


com.CommandText
=
updateSql;
succeed

=
com.ExecuteNonQuery()
>

0
;
}

else

{

//
需要插入

//
WriteString2(insertSql);


com.CommandText
=
insertSql;
succeed

=
com.ExecuteNonQuery()
>

0
;
}


if
(succeed)
{
intOk

++
;
}

else

{
intFail

++
;
}
}

catch
(Exception ex)
{
intFail

++
;
WriteString(com.CommandText);
LogHelper.Error(ex);

break
;
}


#endregion

}


#region
关闭


if
(conn
!=

null

&&
conn.State
!=
ConnectionState.Closed)
{
conn.Close();
}

if
(com
!=

null
)
{
com.Dispose();
}

#endregion

}

catch
(Exception ex)
{
LogHelper.Error(ex);
MessageUtil.ShowError(ex.ToString());
}


if
(intOk
>

0

||
intFail
>

0
)
{

string
tips
=

string
.Format(
"
数据导入成功:{0}个,失败:{1}个
"
, intOk, intFail);
MessageUtil.ShowTips(tips);
}
}
}

以上代码,为了方便,使用了输出脚本的方式进行验证对比,一般情况下也是用得着的。

最后附上该程序的源码,和大家分享学习:
https://files.cnblogs.com/wuhuacong/ImportExcelToOracle.rar

在数据仓库的开发过程中,需要熟悉大量的概念以及相关工具的使用,还需要了解宏观上的各种开发流程,串联起来完成最终的数据仓库项目的开发,本篇介绍一些准备工作,包括涉及到的工具介绍,以及开发过程的描述,记录学习研究的印记,并和大家讨论研究存在的相关问题。

数据仓库的开发,是完全独立于OLTP系统的,也就是独立于当前各种应用的业务系统而作的分析项目,因此要包含从数据的迁移(提取)、变换、清洗、加载等ETL操作,其中可以分为这么几个数据层。

源数据层
客户的各种业务系统中的数据,如包括企业、车辆和司机信息系统、企业录入数据和及营运等数据,里面存放了大量的事务数据。

ODS数据层
数据库用户ODS数据层主要管理把业务数据层的数据存储到ODS数据层,它的数据表主要就是来源于业务数据表,通过一些存储过程把业务数据表结构改变成基层的数据仓库的表结构。

DW数据层

数据库用户DW主要管理把ODS数据层的数据存储到DW数据层,它的数据表主要就是来源于ODS数据表,通过一些存储过程把ODS数据表结构改变成项目主题数据仓库的表结构。
DW数据层还管理一些对存储过程的记录表,方便数据仓库的维护和管理。

ODS是一个面向主题的、集成的、可变的、当前的细节数据集合,用于支持企业对于即时性的、操作性的、集成的全体信息的需求。常常被作为数据仓库的过渡,也是数据仓库项目的可选项之一。

因此操作数据存储(ODS) 是用于支持企业日常的全局应用的数据集合,ODS的数据具有面向主题、集成的、可变的和数据是当前的或是接近当前的4个基本特征。同样也可以看出ODS是介于DB和DW 之间的一种数据存储技术,和原来面向应用的分散的DB相比,ODS中的数据组织方式和数据仓库(DW)一样也是面向主题的和集成的,所以对进入ODS的数 据也象进入数据仓库的数据一样进行集成处理。另外ODS只是存放当前或接近当前的数据,如果需要的话还可以对ODS中的数据进行增、删和更新等操 作,虽然DW中的数据也是面向主题和集成的,但这些数据一般不进行修改,所以ODS和DW的区别主要体现数据的可变性、当前性、稳定性、汇总度上。

为什么需要有一个ODS系统呢?一般在带有ODS的系统体系结构中,ODS都具备如下几个作用:

1) 在业务系统和数据仓库之间形成一个隔离层。

2) 转移一部分业务系统细节查询的功能。

3) 完成数据仓库中不能完成的一些功能。

这里我们就需要用到了IBM的DataStage工具。 DataStage 是一套专门对多种操作数据源的数据抽取、转换和维护过程进行简化和自动化,并将其输入数据集市或数据仓库目标数据库的集成工具。
DataStage可以从多个不同的业务系统中,从多个平台的数据源中抽取数据,完成转换和清洗,装载到各种系统里面。其中每步都可以在图形化工具里完成,同样可以灵活地被外部系统调度,提供专门的设计工具来设计转换规则和清洗规则等,实现了增量抽取、任务调度等多种复杂而实用的功能。其中简单的数据转换可以通过在界面上拖拉操作和调用一些DataStage预定义转换函数来实现,复杂转换可以通过编写脚本或结合其他语言的扩展来实现,并且DataStage提供调试环境,可以极大提高开发和调试抽取、转换程序的效率。
DataStage是基于客户机/服务器的数据集成架构,优化数据收集,转换和巩固的过程。它提供了一套图形化的客户工具:
包括:Designer(设计者):创建执行数据集成任务Job的同时,对数据流和转换过程创建一个可视化的演示。
Manager(管理者):对每个工程的各个单元,包括:库表定义,集中的数据转换,元数据连接等对象进行分类和组织。
Director(指挥者):为启动,停止,监视作业提供交互式控制。
Administrator(管理者):在服务器端管理DataStage的项目和使用者权限的分配

启动Designer,我们可以在其中创建各种数据复制的Job,然后通过串联起来,完成整个业务数据的定时提取工作,所有工作可以在可视化界面上进行操作,还是比较方便的。

下图是对于不同数据源之间的数据复制过程,中间有一个Transformer的变换对象来处理数据列之间的映射关系的。

下图是串联各种Job对象,组装成一个完成的数据复制流程。

完成上面的业务数据整合复制后,就需要通过编写大量的存储过程,完成从ODS数据层到DW数据仓库层的数据清洗加载操作了,一般我们可以在Pl/Sql Developer中完成相关存储过程的编写,如下图所示:

通过存储过程完成数据从ODS层到DW数据仓库层的转移后,下一步就是要围绕数据仓库的模型做各种应用的分析工作了,这时候就需要用到非常强大的BI工具Business Objects了。

在众多的管理信息系统中一般都应用数据仓库技术,进行数据的统计和分析,而数据仓库的建立、使用、管理及维护等方面都需要工具的支持。有些管理信息系统的业务处理数据库同数据仓库系统一起推广使用,因此大大减少了从业务数据库到数据仓库的数据抽取、集成和转换的复杂程度,但无论数据仓库中数据的来源和抽取方式是什么,对数据仓库的维护、管理、分析、统计都需要用客户端报表/分析工具来完成。在众多的联机分析处理产品中,BO公司的Business Object不失为一个好工具。


Business Object具有以下几个特点:前端分析手段灵活,表现方式多样,语义层构建工具应用面广,应用简单,具有文档管理的数据刷新功能,可以利用资料库对不同用户的操作权限进行管理。
Business Object是一个由多个产品组成的产品族,主要包括Business Object,Designer,Document Agent,Supervisor,Business Miner五个产品。这几个产品功能如下:

(1)Bupervisor:Supervisor是BO自身的安全管理工具。在Spervisor中可以建立和维护资料库,在资料库中可以建立BO的用户和用户组,通过对不同用户赋予不同的操作权限,来实现对Business Object的使用用户、定制查询(universe)以及查询报告的安全管理。实际上多个用户定制查询及查询结果的共享时通过资料库实现的。

(2)Designer:Designer是一个多编模型构建工具,它可以将关系库中的表通过类、对象及对象明细等工具映射到多维模型的维、维元素及度量指标上,使用户在符合业务逻辑的多维模型上构建查询。

(3)Business Object:Business Object是一个查询定制和统计报表(图形)生成工具。它利用Designer建立多维模型定制各类查询,查询方式包括Business Object定制查询语义层(universe)、手写SQL语句、存储过程及个人数据文件等,在Business Object 4 1版中还将支持OLAP Pannel,从而使其可以利用其他厂商的OLAP服务器的功能。Business Object将查询结果以多种报表形式及统计图形的方式提交给用户。用户可以在查询结果上进行进一步的分析,如向下/向上钻取(Drill Up/Drill Down)等。另外,在Business Object中还提供了一种script语言,?该语言风格类似Visual Basic,在程序中可以以面向对象的编程风格调用Business Object提供的丰富类库。得script语言可以定制出更为友好的查询界面。

(4)Document Agent:Document Agent是专为工作组用户的文档管理及查询结果刷新而建立的。一组用户对应一个Document Agent,不同用户的查询报表(图形)均可发送到Document Agent供同组的其他用户共享。Document Agent还可以定义统计报表(图形)的刷新频度。

在管理机构由于用户的不同使应用角色与工具也不尽相同:
(1)报表设计人员:负责制作固定报表以外的灵活报表,使用工具为BO;负责根据业务需求定制新的固定报表,使用工具为BO Designer;承担前台报表的分发、管理及刷新,使用工具为BO Document Agent。

(2)普通报表用户和高级报表用户:L浏览固定报表,使用工具为BO Reader;浏览报表设计人员设计的灵活报表,使用工作为BO Reader。

(3)数据仓库管理员:完成数据库管理员(DBA)的正常工作;负责数据仓库数据的刷新维护、用户管理等工作,使用工具主要是数据库的管理工具以及BO Supervisor。

通过BO Designer,完成表之间的语义关系的设计以及相关度量、维度的设计,为报表设计奠定基础。

通过BO控制台,启动InfowView,对报表进行相关的设计工作。

完成以上工作后,报表的前端展示其实是比较方便的,可以通过调用BO的报表生成页面就可以了,给报表生成页面附带相关的参数即可,具体的例子可以参考BO自带的很多相关Demo,在此不再赘述。

上述的内容只是我对数据仓库应用的初步了解和认识,纰漏之处难免,随着学习的进行,认识的深入,应用的开展,项目经验的增加,希望更多东西能够明朗化,深入化,坚固化。

在上篇随笔《
数据仓库开发之路之一--准备工作
》中粗略介绍了一下数据仓库项目应用中涉及到的一些知识和工具,其中涉及到了ETL工具DataStage、BI商务智能工具BO(Business Objects),这两款软件都是一个工具包的集合,包含了很多软件集合的。

数据仓库整体项目流程是一个比较复杂的过程,设计到不少内容,从横向来看,一般可以分为:

1)数据整合层,或者称为操作数据层(ODS),根据项目的需要,可以选用。

2)数据仓库层,也就是所谓的DW层。

3)使用BO的Universe设计的数据库语义层,相关于物理表和报表视图的隔离层。

4)使用BO的Infoview设计报表(如Web Intelligence 报表文档)。

其中1、2是数据准备阶段,3,4是数据应用阶段,准备阶段一般使用ETL工具DataStage和自己编写的各类存储过程来实现,后面的应用阶段,主要使用了BO的套件来完成报表的设计及开发展现等工作。

如一般的横向开发流程示意图如下所示:

在DataStage开发过程中,重头戏是使用DataStage Designer来进行各种任务及任务序列的设计工作,然后利用DataStage Diretor进行相关的调度设计即可。在使用DataStage Designer过程中,总结了一下开发流程,大致如下所示:

1)设计细粒度的复制数据的Server Job,然后串联各类表的Server Job(任务)作为一个主题的Job Sequence(任务序列),最后是串联各主题形成一个大主题的Job Sequence。类似于企业从小组到大部门再到大片区(或更大集权部门)的一种向上集权机制。这样的好处是,我们在调度的时候,只需要调度最大一个Job Sequence即可。

2)建立一个调用存储过程的ETL Job(STP Stage类型),并记录结果。这个ETL Job调用的存储过程就是我们自定义进行数据清理加载到数据库的操作,是指从ODS-》DW的数据操作。

3)串联形成一个完整的数据仓库项目Job,如串联1、2点的相关大主题任务为ODS->ETL。

4)使用Director创建执行时间及周期,这样系统可以自动运行进行相关的调度操作了。

在数据仓库开发设计总,ETL是整个过程的核心灵魂,这里的DataStage做了一部分工作,但是数据的清洗和数据仓库加载的操作还需要自己编写很多存储过程来完成,以便达到灵活处理,方便为何之目的。

这里设计到了数据仓库表、视图等的设计以及存储过程的设计编写操作,因此一般需要遵守好各方面的命名规则及协议,以便达到方便维护管理之目的,各种内容分门别类好,对大量表、视图、存储过程等元数据的管理很有帮助。

这里谈谈ETL设计要点:

1)建立表命名、视图、存储过程等内容的命名规则,如数据表比较多,一般建议采用按用户名Schema的方式来区分不同的数据层,如操作数据层ODS,则可以建立一个ODS的用户Schema来进行管理、数据仓库DW层,则建立DW的用户Schema来进行管理,这样可以在逻辑以及表的命名空间上(物理上)区分不同的内容,实现性能的最大化。如下面是一种规则界定:

1)数据仓库中,表及视图的命名规则:表以
T
开头,视图以
V
开头。下滑线后连接数据仓库主题的拼音缩写,如出租主题即为
C
,客运主题即为
K
,从业人员主题即为
CY
,依此类推。表名及视图名称的主体为汉语拼音,前面两个全拼,后面的以开头字母为缩写。例如:出租的车辆基本信息表为
T_C_CheLiangJBXX
。详见下表。




出租行业

客运行业

货运行业

……

基础表

T_J_XXX

基本信息表

T_C_XXJBXX

T_K_XXJBXX

T_H_XXJBXX



业务事实表

T_C_XX_F

T_K_XX_F

T_H_XX_F



月汇总表

T_C_XX_(F)_Month

T_K_XX_(F)_Month

T_K_XX_(F)_Month



周汇总表

T_C_XX_(F)_Week

T_K_XX_(F)_Week

T_H_XX_(F)_Week



视图

V_C_XXX

V_K_XXX

V_H_XXX




2)如果表数据比较少或者基于其他原因考虑,也可以考虑通过前缀的方式进行区分,如ODS层的数据,如使用ODS_T_Accuse、ODS_T_QIYEPQ这种方式命名、DW层常用时间维度使用DW_DIM_TIME、事实表采用DW_F_H_YUNLIFENXI来表示,DW业务维度采用DW_D_H_DUNWEI来表示。

3)建立不同的Package来存放不同业务范围的存储过程,如维度可用PKG_LOAD_DIM来命名、时间维度可以用PKG_LOAD_DIM_TIME来命名、事实表可以用PKG_LOAD_FACT_TAXI来命名等,包体里面的存储过程要清晰,附带必要的功能说明,最好在文档的一个表格中详细说明包体及存储过程函数的各种信息,如数据库详细说明那样(PKG是业务定义字母,不同业务用不同的区分)。

4)如果采用不同用户Schema来区分不同的数据层的做法,那么需要为DW层、ODS层指定不同的表空间及存储大小等,以便利用不同物理存储带来的性能方面的提升。

如下面是一个存储过程的组织实例图:

设计好数据仓库的相关内容后,接着就是需要进行另外一个重要的操作,就是语义设计和报表设计了,这两项工作是通过BO工具进行完成,前期的工作都是为后面两步进行准备的。

Universe设计,就是在语义层指定表自己的关系,主题的度量值、维之间的层次关系(以便实现报表向上向下钻取的操作),Universe设计,其实就是在报表的使用对象和实际的表对象之间建立一个逻辑对应关系以及隔离关系。

做好这些,就需要在BO的InfoView里面设计好相关的报表,通过选定Universe文件后,然后再主界面中拖拉各种维度以及度量值,就可以很方面的创建各类报表,图表则拖拉报表模板进去,然后拖动维度和度量到模板中,加载数据就可以实现报表的数据显示了,如下所示:

先总结写到这里,有心得再继续记录了。