Chinaunix首页 | 论坛 | 博客
  • 博客访问: 90924683
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924674
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924685
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924686
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924687
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924688
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924689
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924690
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924691
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924692
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924693
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924694
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924695
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924696
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924697
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924698
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924689
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924700
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924701
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924702
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924703
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924704
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924705
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924706
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924707
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924708
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924709
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924710
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924711
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924712
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924713
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924704
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924715
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924716
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924717
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924718
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924719
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924720
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924721
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924722
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924723
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924724
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924725
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924726
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924727
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924728
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924719
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924730
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924731
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924732
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924733
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks

DB2 9 应用开发(733 考试)认证指南,第 4 部分: 嵌入式 SQL 编程(3)-sdccf-ChinaUnix博客
  • 博客访问: 90924734
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-31 23:28:43

构建与 DB2 进行交互的应用程序

developerWorks



构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219373) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219372) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219371) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219370) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219369) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219368) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219367) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219366) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219365) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219364) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219363) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219362) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219361) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219360) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219359) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219358) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219357) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219356) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219355) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219354) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219353) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219352) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219351) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219350) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219349) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219348) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219347) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219346) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219345) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219344) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219343) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219342) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219341) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219340) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219339) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219338) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219337) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219336) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219335) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219334) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219333) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219332) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219331) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219330) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219329) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219328) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219327) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219326) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219325) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219324) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219323) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~


构造嵌入式 SQL 应用程序

在前面,我们了解到 DB2 Database Manager 依靠宿主变量在应用程序和数据库之间移动数据。我们还知道要在称为声明段的专用区域中定义宿主变量,以此区分宿主变量和其他高级编程语言变量。那么,该如何编写声明段呢?

声明段的开头由 BEGIN DECLARE SECTION SQL 语句定义,而结尾由 END DECLARE SECTION 语句定义。因此,C/C++ 源代码文件中典型的声明段如下所示:

...
// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    char    EmployeeID[7];
    char    WorkDept[4];
    char    Job[9];
    char    Sex[2];
    double  Salary;
    double  Bonus;
    double  Commision;
EXEC SQL END DECLARE SECTION;
...
				

在源代码文件中,在可以编写高级编程语言变量声明的任何地方,都可以编写声明段。虽然一个源代码文件通常只包含一个声明段,但多个声明段也是允许的。将数据传输到数据库中的宿主变量称作输入宿主变量(input host variable),而从数据库接收数据的宿主变量则称作输出宿主变量(output host variable)。不管宿主变量是用于输入还是输出,其属性都必须适合于使用它的上下文。因此,在定义宿主变量时,必须使其数据类型和长度与它们将要操作的列的数据类型和长度相兼容。另外,必须给应用程序中使用的每个宿主变量指定一个惟一的名称,同一文件中不允许使用重复的名称,即使在使用多个声明段的情况下也是如此。一个名为 Declaration Generator 的工具可以用于为数据库中给定表的列生成宿主变量声明。该工具创建嵌入式 SQL 声明源代码文件,这些文件可轻松地插入 C/C++、Java 语言、COBOL 和 FORTRAN 应用程序中。关于该实用工具的更多信息,请查阅 DB2 UDB Command Reference 中的 db2dclgn 命令。如何使用宿主变量在应用程序和数据库之间移动数据呢?回答该问题最简单的方式是查看一个简单的嵌入式 SQL 源代码片段,比如清单 1 中的代码段。



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    char     LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Retrieve A Record From The Database
EXEC SQL SELECT empno, lastname 
    INTO :EmployeeNo, :LastName 
    FROM employee 
    WHERE empno = '000100';
  
// Do Something With The Results
...
	

在这个示例中,当执行 SQL 语句 SELECT empno, lastname FROM employee WHERE empno = '000100' 时,结果被传输给宿主变量 EmployeeNoLastName。在对源代码文件进行预编译时,会添加执行这一数据传输的实际命令和函数。





回页首


默认情况下,DB2 数据库表中的列可以包含空(null)值。因为空值的存储方式与常规数据不同,所以如果应用程序打算使用 null 数据,就必须采取特殊措施。空值不能像其他数据值一样被检索和复制到宿主变量中,而是必须检查一个特殊标志以判断特定值是否为空。为了获得该标志的值,必须将名为指示器变量(或 null 指示器变量)的专用变量与一个可空列的宿主变量关联起来。

因为指示器变量必须让 DB2 Database Manager 和应用程序都能访问到,所以必须在声明段中定义它们。还必须给它们指派与 DB2 UDB SMALLINT 数据类型兼容的数据类型。因此,C/C++ 源代码文件中用于定义指示器变量的代码如下所示:

// Define The SQL Host Variables Needed 	
EXEC SQL BEGIN DECLARE SECTION;
    short   SalaryNullIndicator;
EXEC SQL END DECLARE SECTION;
        

当指示器变量在 SQL 语句中紧跟着一个特定的宿主变量出现时,它就与该宿主变量相关联。指示器变量一旦与宿主变量相关联,那么在填充与之相应的宿主变量以后,就可以检查它。如果指示器变量包含的是一个负值,就表示找到了一个空值,并应该忽略其相应的宿主变量值。否则,相应的宿主变量值就是有效的。

同样,为了理解如何使用指示器变量,我们来看一个嵌入式 SQL 源代码段。清单 2 是用 C 编程语言编写的代码,它展示了如何定义和使用指示器变量:



                    
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char     EmployeeNo[7];
    double   Salary;    // Salary - Used If SalaryNI Is Positive ( >= 0 )
    short    SalaryNI;  // Salary NULL Indicator - Used
                        // To Determine If Salary
                        // Value Should Be NULL
EXEC SQL END DECLARE SECTION;
...

// Declare A Static Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR SELECT empno, DOUBLE(salary)
    FROM employee;

// Open The Cursor
EXEC SQL OPEN cursor1;
    
// If The Cursor Was Opened Successfully, Retrieve And
// Display All Records Available
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve The Current Record From The Cursor
    EXEC SQL FETCH cursor1 INTO :EmployeeNo, :Salary :SalaryNI;

    // if The Salary Value For The Record Is NULL, ...
    if (SalaryNI < 0)
    {
        printf("No salary information is available for ");
        printf("employee %s\n", EmployeeNo);
    }  
}
  
// Close The Open Cursor
EXEC SQL CLOSE C1;
...
	

指示器变量还可以在执行插入或更新操作时,用于向数据库发送空值。在处理 INSERTUPDATE SQL 语句时,DB2 Database Manager 首先检查所提供的所有指示器变量的值;如果一个或多个指示器变量包含一个负值,它就给相应的列指派 null 值,假设该列支持空值。(如果指示器变量被设置为零或包含一个正数,或者如果未使用指示器变量,那么 DB2 Database Manager 将相应的宿主变量中存储的值指派给相应的列。)因此,C/C++ 源代码文件中用于给表中一列指派 null 值的代码如下所示:

ValueInd = -1;
EXEC SQL INSERT INTO tab1 VALUES (:Value :ValueInd);
        





回页首


到目前为止,我们只研究了如何使用宿主变量和指示器变量在嵌入式 SQL 应用程序和数据库对象之间移动数据。然而,嵌入式 SQL 应用程序有时候需要与 DB2 Database Manager 本身进行通信。两种特殊的 SQL 数据结构用于建立这种重要的通信链接:SQL Communications Area(SQLCA)数据结构和 SQL Descriptor Area(SQLDA)数据结构。

SQLCA 数据结构包含每当执行 SQL 语句或 DB2 管理 API 函数时,由 DB2 Database Manager 更新的元素集合。为了让 DB2 Database Manager 填充该数据结构,就必须首先存在该结构。因此,任何包含嵌入式 SQL 或者要调用一个或更多管理 API 的应用程序必须定义至少一个 SQLCA 数据结构变量。实际上,如果不存在 SQLCA 数据结构变量,应用程序就无法成功编译。

那么,SQLCA 数据结构变量是什么样的呢?表 1 列出了构成 SQLCA 数据结构变量的元素。



元素名称 数据类型 描述
sqlcaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLCA"。如果在解析 SQL 过程体时返回了行号信息,那么第 6 个字节包含字符 L
sqlcabc INTEGER SQLCA 数据结构本身的大小,以字节为单位。该元素总是应该包含值 136
sqlcode INTEGER SQL 返回码值。值为 0 表示执行成功,正值表示执行成功,但发出警告,而负值则表示出错。请查阅 DB2 UDB Message Reference 产品手册第 1 和 2 卷(参见 参考资料),获得关于特定 SQL 返回码值的更多信息。
sqlerrml SMALLINT 该结构的 sqlerrmc 元素中所存储的数据大小,以字节为单位。该值可以是 070 之间的任何数字;值为 0 表示 sqlerrmc 字段中未存储任何数据。
sqlerrmc CHAR(70) 由值 "0xFF" 分隔的一个或多个错误消息标志,它们将取代警告或错误情况描述中的变量。当成功建立连接时,也要使用该元素。
sqlerrp CHAR(8) 表示当前所使用的 DB2 服务器类型的诊断值。该值以标识产品版本和发布的三字母编码开始,紧接着是标识该产品修改级别的 5 位数字。例如,SQL09010 意味 DB2 版本 9,发布 1,修改级别 0。如果 sqlcode 元素包含一个负值,那么该元素将包含一个标识报告错误的模块的 8 字符编码。
sqlerrd INTEGER ARRAY 由六个整型值组成的数组,它在发生错误时提供附加的诊断信息(关于该元素中可以返回的诊断信息的更多信息,请查阅 表 2)。
sqlwarn CHAR(11) 一个由用作警告指示器的字符值组成的数组;数组的每个元素要么包含一个空格,要么包含字母 W。如果使用了复合 SQL,该字段将包含一些警告指示器,它们是为复合 SQL 语句块中执行的所有子语句设置的。(关于该元素中可以返回的警告信息类型的更多信息,请查阅 表 3。)
sqlstate CHAR(5) 标识最近执行的 SQL 语句结果的 SQLSTATE 值。(我们将在 SQLSTATE 一节中详细讨论 SQLSTATE 值。)

表 2 给出了 sqlca.sqlerrd 数组中的元素。



数组元素 描述
sqlerrd[0]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果成功执行了 SQL 存储过程,这个元素就包含过程的返回状态。

sqlerrd[1]

如果成功建立了连接,那么在将混合字符数据(CHAR 数据类型)从应用程序代码页转换成数据库代码页时,该元素将包含该数据预期的长度差。值为 01 表示不需要进行扩展;大于 1 的正值表示可能要扩展的长度;而负值则表示可能要减小的长度。

如果 SQLCA 数据结构包含 NOT ATOMIC 复合 SQL 语句的信息,该元素将包含遇到错误的子语句的数目(如果有子语句失败的话)。

sqlerrd[2]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,那么当所连接的数据库为可更新的时候,该元素将包含值 1;当所连接的数据库为只读的时候,则包含值 2

如果 SQLCA 数据结构包含成功执行的 PREPARE SQL 语句的信息,该元素将包含对于执行所准备的语句时结果数据集中将返回的行数的估计值。

如果 SQLCA 数据结构包含成功执行的 INSERTUPDATEDELETE SQL 语句的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含成功执行的 OPEN SQL 语句以及用来执行插入、更新和删除操作的游标的信息,该元素将包含该操作所影响的行数。

如果 SQLCA 数据结构包含在解析 SQL 过程体时遇到错误的 CREATE PROCEDURE SQL 语句的信息,该元素将包含遇到错误之处的行号。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含受到复合 SQL 语句块中子语句影响的行数。

sqlerrd[3]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用来自下级客户机的一阶段提交(commit)时,该元素将包含值 0;当使用一阶段提交时,将包含值 1;当使用一阶段只读提交时,将包含值 2;而当使用两阶段提交时,则会包含值 3

如果 SQLCA 数据结构包含执行成功的 PREPARE SQL 语句的信息,该元素将包含准备指定语句所需资源的相对成本估计。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的子语句数量。

sqlerrd[4]

如果 SQLCA 数据结构包含成功执行的 CONNECT SQL 语句的信息,当使用服务器身份验证时,该元素将包含值 0;当使用客户机身份验证时,将包含值 1;当由 DB2 Connect 来进行身份验证时,将包含值 2;当使用 SERVER_ENCRYPT 身份验证时,将包含值 4;当使用带加密身份验证的 DB2 Connect 时,将包含值 5;当使用 KERBEROS 身份验证时,将包含值 7;当使用 GSSPLUGIN 身份验证时,将包含值 9;当使用 DATA_ENCRYPT 身份验证时,将包含值 11;而当无法确定身份验证的处理方式时,将包含值 255

如果 SQLCA 数据结构包含任何其他信息,该元素将包含已插入、更新或删除的总行数,而这些插入、更新或删除操作是由一个或多个引用完整性约束的 DELETE 规则或由一个或更多触发器的激活导致的。

如果 SQLCA 数据结构包含复合 SQL 语句的信息,该元素将包含复合 SQL 语句块中成功执行的每个子语句对应的这种行的总数。

sqlerrd[5] 对于分区数据库,该元素包含遇到错误或警告的分区的分区号。如果未遇到错误或警告,该元素将包含用作协调节点的分区的分区号。

最后,表 3 描述了 sqlca.sqlwarn 数组的元素。



数组元素 描述
sqlwarn[0] 如果该数组中的所有其他元素都为空,该元素就为空;如果有一个或多个其他元素不为空,它就包含字符 W
sqlwarn[1]

在将字符串数据类型的列值指派给宿主变量时,如果截断了字符串值,该元素就包含字符 W

如果截断了字符串的 null 终止符,它就包含字符 N

如果 CONNECTATTACH 操作成功执行,而且连接的授权名长于 8 字节,它就包含字符 A

如果 sqlerrd[3] 中存储的 PREPARE 语句相对成本估计值大于 INTEGER 数据类型可以存储的数量或者小于 1,而且 CURRENT EXPLAIN MODECURRENT EXPLAIN SNAPSHOT 特殊寄存器设置为 NO 之外的其他值,它就包含字符 P

sqlwarn[2]

如果清除了传递给函数的参数中的 null 值,该元素就包含字符 W

如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,那么在数据库处于静默状态时,该元素就包含字符 D;在实例处于静默状态时,该元素就包含字符 I

sqlwarn[3]

如果所检索值的数目不等于所提供的宿主变量的数目,该元素就包含字符 W

如果用 ASSOCIATE LOCATORS SQL 语句指定的结果数据集定位器数目少于调用的过程所返回的结果数据集的实际数目,该元素就包含字符 Z

sqlwarn[4] 如果所准备的 UPDATEDELETE SQL 语句不包含 WHERE 从句,该元素就包含字符 W
sqlwarn[5] 如果在 SQL 语句执行期间容忍了一个错误,该元素就包含字符 E
sqlwarn[6] 如果调整了日期的计算结果以避免无效的日期值,该元素就包含字符 W
sqlwarn[7] 如果 SQLCA 数据结构包含执行成功的 CONNECT SQL 语句的信息,而且启用了 DYN_QUERY_MGMT 数据库配置参数,该元素就包含字符 E
sqlwarn[8] 如果用替换字符取代了无法进行转换的字符,该元素就包含字符 W
sqlwarn[9] 如果在处理列函数期间忽略了算术表达式中的一个或多个错误,该元素就包含字符 W
sqlwarn[10] 如果在转换 SQLCA 数据结构变量的另一个元素中的字符数据值时发生了转换错误,该元素就包含字符 W

SQLCA 数据结构变量和 SQLDA 数据结构变量(下面将详细讨论)可以通过在嵌入式 SQL 源代码文件中嵌入适当形式的 INCLUDE SQL 语句(分别使用 INCLUDE SQLCAINCLUDE SQLDA)来创建。





回页首


SQL Descriptor Area(SQLDA)数据结构包含用来向 PREPAREOPENFETCHEXECUTE SQL 语句提供详细信息的元素集合。该数据结构包含一个头以及一个数组结构,该数组中的每个元素描述一个宿主变量或结果数据集中的一列。表 4 列出了构成 SQLDA 数据结构变量的元素。



元素名称 数据类型 描述
sqldaid CHAR(8) 信息转储的可视标志。为了帮助可视化地识别该数据结构,该元素通常包含值 "SQLDA"。这个字段的第 7 个字节是称为 SQLDOUBLED 的标志;若为每个列创建了两个 sqlvar 条目,或者描述的任何宿主变量是结构类型,那么 DB2 Database Manager 将这个字节设置为 2,否则设置为 BLOBCLOBDBCLOB 数据类型。
sqldabc INTEGER QLDA 数据结构本身的大小,以字节为单位。指派给该元素的值要通过算式 sqldabc = 16 + (44 * sqln)(对于 32 位操作系统)或 sqldabc = 16 + (56 * sqln)(对于 64 位操作系统)来确定。
sqln SMALLINT sqlvar 数组中元素的总数目。
sqld SMALLINT 该元素可以指出下列两者之一:由 DESCRIBEPREPARE SQL 语句返回的结果数据集中列的数目,或由 sqlvar 数组中元素描述的宿主变量的数目。
sqlvar STRUCTURE ARRAY 包含宿主变量或结果集列的有关信息的数据结构数组。

除了这些基本信息之外,SQLDA 数据结构变量还包含任意数目的 sqlvar 数据结构(也称为 SQLVAR 变量)。每个 SQLVAR 变量中所存储的信息取决于使用相应的 SQLDA 数据结构变量的位置:当与 PREPAREDESCRIBE SQL 语句一起使用时,每个 SQLVAR 变量将包含执行准备的 SQL 语句时所产生的结果数据集中的列的有关信息。(如果存在具有大型对象(LOB)或用户定义数据类型的列,那么所使用的 SQLVAR 变量的数目将增加一倍,并将给 SQLDA 数据结构变量的 sqldaid 元素中存储的字符串值的第 7 个字节指派值 2。)另一方面,当 SQLDA 数据结构变量与 OPENFETCHEXECUTE SQL 语句一起使用时,每个 SQLVAR 变量将包含在执行语句期间其值将传递给 DB2 Database Manager 的宿主变量的有关信息。

可以使用两类 SQLVAR 变量:基本 SQLVAR 和辅助 SQLVAR。基本 SQLVAR 包含结果数据集列或宿主变量的基本信息(例如数据类型代码、长度属性、列名、宿主变量地址和指示器变量地址)。表 5 列出了构成基本 SQLVAR 数据结构变量的元素。



元素名称 数据类型 描述
sqltype SMALLINT 所使用的宿主变量/参数标记的数据类型,或所产生的结果数据集中列的数据类型。
sqllen SMALLINT 所使用的宿主变量的长度,或所产生的结果数据集中列的大小,以字节为单位。
sqldata Pointer 指向用于所使用的宿主变量的数据在内存中存储位置的指针,或指向用于所产生的结果数据集中列的数据将在内存中存储的位置的指针。
sqlind Pointer 指向与所使用的宿主变量相关联的 null 指示器变量的数据在内存中存储位置的指针,或指向与所产生的结果数据集中列相关联的 null 指示器变量的数据在内存中存储的位置的指针。
sqlname VARCHAR(30) 宿主变量或所产生的结果数据集中列的未限定名。

另一方面,辅助 SQLVAR 包含用于不同数据类型的数据类型名,或者包含列或宿主变量的长度属性和指向包含 LOB 数据类型数据的真正长度的缓冲区的指针。只有在 SQLVAR 条目的数量翻倍(由于要使用 LOB 或不同的数据类型)时,才出现辅助 SQLVAR 条目:如果使用定位器(locator)或文件引用变量表示 LOB 数据类型,就不需要辅助 SQLVAR 条目。

可以手工放置(使用适当的编程语言语句)SQLDA 数据结构变量中存储的信息,以及任何相应的 SQLVAR 变量中存储的信息,也可以通过执行 DESCRIBE SQL 语句自动生成。





回页首


为了对数据库执行任何类型的操作,必须首先建立到数据库的连接。在嵌入式 SQL 应用程序中,通过执行 CONNECT SQL 语句来建立(某些情况下为终止)数据库连接(CONNECT 语句的 RESET 选项用于终止连接)。在连接过程中,建立连接所需的信息(例如授权 ID 和授权用户相应的口令)将被传递给指定的数据库以进行有效性检验。通常,在应用程序运行时收集该信息,并通过一个或多个宿主变量将其传递给 CONNECT 语句。因此,C/C++ 源代码文件中通常用于建立数据库连接的代码如清单 3 所示。



                    
...
// Define The SQL Host Variables Needed	
EXEC SQL BEGIN DECLARE SECTION;
    char   DataSource[129] = {"SAMPLE"};
    char   UserID[129] = {"USER1"};
    char   Password[129] = {"User1PWD"};
EXEC SQL END DECLARE SECTION;

...
// Connect To The Appropriate Database
EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
...

// Terminate The Database Connection
EXEC SQL CONNECT RESET;
...
	

嵌入式 SQL 应用程序可以使用两种不同类型的连接语义。这两种类型简称为 Type 1Type 2,它们支持两种差异很大的行为:Type 1 连接只支持每个事务连接一个数据库(称为远程工作单元),而 Type 2 连接则支持每个事务连接多个数据库(称为应用程序导向的分布式工作单元)。实际上,当使用 Type 1 连接时,应用程序一次只能连接一个数据库。一旦建立了到数据库的连接并启动事务,必须提交或回滚该事务,然后才能建立另一个数据库连接。另一方面,当使用 Type 2 连接时,应用程序可以同时连接几个不同的数据库,每个数据库连接都将拥有它自己的事务集合。

嵌入式 SQL 应用程序真正使用的连接语义类型要由应用程序预编译时指派给 CONNECTSQLRULESDISCONNECTSYNCPOINT SQL 预编译器选项的值来确定。





回页首


当在应用程序中嵌入静态 SQL 语句时,在遇到它们时就执行。但是,当使用动态 SQL 语句时,则可以用以下两种方式之一处理它们:

  • 准备和执行:这种方法将 SQL 语句的准备与它的真正执行分隔开,当要重复执行 SQL 语句时,通常使用该方法。当应用程序需要预先知道在执行 SELECT SQL 语句时所产生的结果数据集中存在的列的有关信息时,也使用该方法。SQL 语句 PREPAREEXECUTE 用于以该方式处理动态 SQL 语句。

  • 立即执行:这种方法将 SQL 语句的准备和执行组合成一步,当 SQL 语句只执行一次时,通常使用该方法。当应用程序不需要关于在执行 SQL 语句时将会产生的结果数据集(如果有的话)的附加信息时,也使用该方法。SQL 语句 EXECUTE IMMEDIATE 用于以该方式处理动态 SQL 语句。

在运行时准备和执行(使用任何一种方法)的动态 SQL 语句不允许包含对宿主变量的引用。但是,它们可以包含代替常数和表达式的参数标记。参数标记由问号(?)字符来表示,它们指明在执行该语句时,将在 SQL 语句中什么地方替换一个或多个宿主变量的当前值(或 SQLDA 数据结构变量的元素)。因此,如果所执行的 SQL 语句为静态的,则通常在引用宿主变量的地方使用参数标记。可以使用两种类型的参数标记:类型化的和非类型化的。

类型化的参数标记是与在 SQL 语句中引用它时使用的目标数据类型一起指定的。类型化参数标记具有下列形式:

CAST( ? AS DataType )
        

这种表示法并不意味着要调用函数;而是预示在应用程序运行时替代参数标记的值的数据类型将为指定的数据类型,或者是可转换成指定数据类型的一种数据类型。例如,考虑下列 SQL 语句:

UPDATE employee SET lastname = CAST(? AS VARCHAR(12)) WHERE empno = '000050'
        

在这个示例中,LASTNAME 列的值将在应用程序运行时提供,该值的数据类型将为 VARCHAR(12),或者是可转换成 VARCHAR(12) 的一种数据类型。

另一方面,非类型化的参数标记无需指定其目标数据类型,其表示形式只是一个问号(?)。非类型化参数标记的数据类型是通过使用它的上下文来确定的。例如,在下列 SQL 语句中,LASTNAME 列的值在应用程序运行时提供,并假设该值的数据类型将与指派给 EMPLOYEE 表中 LASTNAME 列的数据类型相兼容。

UPDATE employee SET lastname = ? WHERE empno = '000050'
	

当参数标记用在嵌入式 SQL 应用程序中时,若 EXECUTEEXECUTE IMMEDIATE SQL 语句用于执行所指定的 SQL 语句,就必须将用来替代参数标记的值作为附加参数提供给它们。清单 4 是用 C 编程语言编写的示例,它说明了如何将实际值提供给简单的 UPDATE SQL 语句中的参数标记。



                              
...
// Define The SQL Host Variables Needed
EXEC SQL BEGIN DECLARE SECTION;
    char   SQLStmt[80];
    char   JobType[10];
EXEC SQL END DECLARE SECTION;
...
	     
// Define A Dynamic UPDATE SQL Statement That Uses A Parameter Marker
strcpy(SQLStmt, "UPDATE employee SET job = ? ");
strcat(SQLStmt, "WHERE job = 'DESIGNER'");
	         
// Populate The Host Variable That Will Be Used In Place Of The Parameter Marker
strcpy(JobType, "MANAGER");
	         
// Prepare The SQL Statement
EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
	         
// Execute The SQL Statement
EXEC SQL EXECUTE SQL_STMT USING :JobType;
...
	





回页首


无论在嵌入式 SQL 应用程序中使用的是静态 SQL 语句还是动态 SQL 语句,只要执行了该语句,就必须检索和处理所产生的结果。如果该 SQL 语句不是 SELECTVALUES 语句,执行之后就只需检查 SQLCA 数据结构变量,以确保该语句按预期执行。但是,如果执行的是查询,尤其是返回多行的查询,就需要附加的步骤,从而从所产生的结果数据集中检索数据。

当查询向应用程序返回多行时,DB2 使用称为 “游标(cursor)” 的机制从所产生的结果数据集中检索数据值。“游标” 这个名称可能源自在早期计算机屏幕上看到的闪烁光标。正如光标指示出屏幕上的当前位置并识别下面输入的单词将出现的位置一样,DB2 游标可用来指示出结果数据集中的当前位置(例如,当前行),并识别将返回给应用程序的下一个数据行。如果要将游标包含在嵌入式 SQL 应用程序中,就必须依次执行下列步骤:

  1. 声明(定义)一个游标及其类型(只读的或可更新的),并将其与所需的查询关联起来。这是通过执行 DECLARE CURSOR 语句来完成的。

  2. 打开游标。这将导致执行相应的查询并产生结果数据集。这是通过执行 OPEN 语句来完成的。

  3. 一行行地检索(读取)结果数据集中存储的每一行,直到出现数据结束的条件。这是通过重复执行 FETCH 语句来完成的;宿主变量或 SQLDA 数据结构变量与 FETCH 语句结合使用,从结果数据集中提取一行数据。每当从结果数据集中检索一行时,游标就自动地移到下一行。

  4. 在适当条件下,可以修改或删除当前行,但是仅当游标为可更新游标时才能这么做。这是通过执行 UPDATE 语句或 DELETE 语句来完成的。

  5. 关闭游标。这个操作导致删除执行相应查询时所产生的结果数据集。这是通过执行 CLOSE 语句来完成的。

既然我们已经了解了为使用游标必须要执行的一些步骤,现在就来讨论如何在应用程序中编写这些步骤。清单 5 是用 C 编程语言编写的示例,它说明了如何使用游标来检索一个简单查询的结果。



                      
...
// Declare The SQL Host Memory Variables
EXEC SQL BEGIN DECLARE SECTION;
    char      EmployeeNo[7];
    char      LastName[16];
EXEC SQL END DECLARE SECTION;
...

// Declare A Cursor
EXEC SQL DECLARE cursor1 CURSOR FOR
    SELECT empno, lastname
    FROM employee
    WHERE job = 'DESIGNER';

// Open The Cursor
EXEC SQL OPEN cursor1;

// Fetch The Records
while (sqlca.sqlcode == SQL_RC_OK)
{
    // Retrieve A Record
    EXEC SQL FETCH cursor1
    INTO :EmployeeNo, :LastName;

    // Process The Information Retrieved
    if (sqlca.sqlcode == SQL_RC_OK)
        ...
}

// Close The Cursor
EXEC SQL CLOSE cursor1;
...
	

如果事先知道对查询的响应只会产生一行数据,就可以通过执行 SELECT INTO 语句或 VALUES INTO 语句将该行内容直接复制到宿主变量中。与 SELECT 语句一样,SELECT INTO 语句也可以用于构造复杂的查询。但与 SELECT 语句不同的是,SELECT INTO 语句的语法中需要提供一系列有效的宿主变量,无法动态地使用它。此外,如果执行 SELECT INTO 语句时产生的结果数据集中包含多条记录,该操作就将失败,并生成一个错误。(如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)

SELECT INTO 语句一样,VALUES INTO 语句可以用于检索与一条记录相关联的数据,并将其复制到一个或多个宿主变量中。另外,与 SELECT INTO 语句一样,当执行 VALUES INTO 语句时,检索到的所有数据都存储在结果数据集中。如果这个结果数据集只包含一条记录,就将该记录中的第一个值复制到所指定的第一个宿主变量中,将第二个值复制到所指定的第二个宿主变量中,依次类推。如果所产生的结果数据集中包含多条记录,该操作将失败,并生成一个错误。(同样,如果所产生的结果数据集为空,就生成一个 NOT FOUND 警告。)





回页首


事务(也称为工作单元)是组合成一个单元的一个或多个 SQL 操作的序列,通常包含在一个应用程序进程中。一个给定的事务可以包含从一个操作到成百甚至上千个任意数目的 SQL 操作,这取决于在业务逻辑中哪些步骤可以被组合为单一步骤。

事务的启动和终止定义了数据库中的数据一致性点:要么将事务中执行的所有操作的结果持久地应用到数据库中(提交),要么撤消所有操作的结果(回滚),并使数据库返回到启动事务之前所处的状态。在大多数情况下,在建立了到数据库的连接之后,或在紧接着前一个事务终止之后,第一次执行一条可执行的 SQL 语句时启动事务。一旦启动事务,就可以使用自动提交特性隐式地终止事务,还可以通过执行 COMMITROLLBACK SQL 语句显式地终止事务。如果启用了自动提交特性,每个可执行的 SQL 语句就被视为单独的事务;如果该语句执行成功,那么它所进行的任何修改都将应用到数据库中;如果该语句失败,则会丢弃所有修改。





回页首


既然我们已经讨论了用于构造嵌入式 SQL 应用程序的一些基本组件,现在就来看一看如何将它们组合成嵌入式 SQL 应用程序来与 DB2 数据库进行交互。清单 6 给出了一个用 C 编程语言编写的使用静态 SQL 的简单嵌入式 SQL 应用程序,它获得并输出工作头衔为 “DESIGNER” 的所有雇员的雇员标识号、姓和薪水。



                     
  
  #include 
  #include 
  #include 
  
  int main()
  {
      // Include The SQLCA Data Structure Variable
      EXEC SQL INCLUDE SQLCA;
  
      // Define The SQL Host Variables Needed
      EXEC SQL BEGIN DECLARE SECTION;
          char     EmployeeNo[7];
          char     LastName[16];
          double   Salary;
          short    SalaryNI;
      EXEC SQL END DECLARE SECTION;
      
      // Connect To The Appropriate Database
      EXEC SQL CONNECT TO sample USER db2admin USING ibmdb2;
      
      // Declare A Static Cursor
      EXEC SQL DECLARE cursor1 CURSOR FOR
          SELECT empno,
                 lastname,
                 DOUBLE(salary)
          FROM employee
          WHERE JOB = 'DESIGNER';
          
      // Open The Cursor
      EXEC SQL OPEN cursor1;
      
      // If The Cursor Was Opened Successfully, Retrieve And
      // Display All Records Available
      while (sqlca.sqlcode == SQL_RC_OK)
      {
          // Retrieve The Current Record From The Cursor
          EXEC SQL FETCH cursor1
              INTO :EmployeeNo,
                   :LastName,
                   :Salary :SalaryNI;
      
          // Display The Record Retrieved
          if (sqlca.sqlcode == SQL_RC_OK) 
          { 
          	printf("%-8s %-16s ", EmployeeNo,
      	        LastName);
          	if (SalaryNI >= 0)  
      	        printf("%lf\n", Salary); 
          	else
      	        printf("Unknown\n"); 
          }
      }
      
      // Close The Open Cursor
      EXEC SQL CLOSE cursor1;
  
      // Commit The Transaction
      EXEC SQL COMMIT;
  
      // Terminate The Database Connection
      EXEC SQL CONNECT RESET;
      
      // Return Control To The Operating System
      return(0);
  }    
        

清单 7 给出了一个用 C 编程语言编写的使用动态 SQL 的简单嵌入式 SQL 应用程序,它将工作头衔为 “DESIGNER” 的所有职员的工作头衔改为 “MANAGER”。



                     
	
#include 
#include 
#include 

int main()
{
    // Include The SQLCA Data Structure Variable
    EXEC SQL INCLUDE SQLCA;

    // Define The SQL Host Variables Needed
    EXEC SQL BEGIN DECLARE SECTION;
        char     DataSource[129] = {"SAMPLE"};
        char     UserID[129] = {"USER1"};
        char     Password[129] = {"User1PWD"};
        char     SQLStmt[80];
        char     JobType[10];
    EXEC SQL END DECLARE SECTION;
    
    // Connect To The Appropriate Database
    EXEC SQL CONNECT TO :DataSource USER :UserID USING :Password;
       
    // Define A Dynamic UPDATE SQL Statement That Uses A
    // Parameter Marker
    strcpy(SQLStmt, "UPDATE employee SET JOB = ? ");
    strcat(SQLStmt, "WHERE job = 'DESIGNER'");
    
    // Populate The Host Variable That Will Be Used In
    // Place Of The Parameter Marker
    strcpy(JobType, "MANAGER");
    
    // Prepare The SQL Statement
    EXEC SQL PREPARE SQL_STMT FROM :SQLStmt;
    
    // Execute The SQL Statement
    EXEC SQL EXECUTE SQL_STMT USING :JobType;
    
    // Commit The Transaction
    EXEC SQL COMMIT;

    // Terminate The Database Connection
    EXEC SQL DISCONNECT CURRENT;
    
    // return Control To The Operating System
    return(0);
}   
     
阅读(219322) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~